Copy nautilus-places-sidebar.[ch] as gtkplacessidebar.[ch]
authorFederico Mena Quintero <federico@gnome.org>
Tue, 6 Sep 2011 22:53:30 +0000 (17:53 -0500)
committerFederico Mena Quintero <federico@gnome.org>
Tue, 4 Sep 2012 01:38:03 +0000 (20:38 -0500)
gtk/gtkplacessidebar.c [new file with mode: 0644]
gtk/gtkplacessidebar.h [new file with mode: 0644]

diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c
new file mode 100644 (file)
index 0000000..d8f6bfb
--- /dev/null
@@ -0,0 +1,3457 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/*
+ *  Nautilus
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Authors : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk)
+ *            Cosimo Cecchi <cosimoc@gnome.org>
+ *
+ */
+#include <config.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <libnautilus-private/nautilus-dnd.h>
+#include <libnautilus-private/nautilus-bookmark.h>
+#include <libnautilus-private/nautilus-global-preferences.h>
+#include <libnautilus-private/nautilus-module.h>
+#include <libnautilus-private/nautilus-file.h>
+#include <libnautilus-private/nautilus-file-utilities.h>
+#include <libnautilus-private/nautilus-file-operations.h>
+#include <libnautilus-private/nautilus-trash-monitor.h>
+#include <libnautilus-private/nautilus-icon-names.h>
+
+#include <eel/eel-debug.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-graphic-effects.h>
+#include <eel/eel-string.h>
+#include <eel/eel-stock-dialogs.h>
+
+#include "nautilus-application.h"
+#include "nautilus-bookmark-list.h"
+#include "nautilus-places-sidebar.h"
+#include "nautilus-window.h"
+#include "nautilus-window-slot.h"
+
+#define DEBUG_FLAG NAUTILUS_DEBUG_PLACES
+#include <libnautilus-private/nautilus-debug.h>
+
+#define EJECT_BUTTON_XPAD 6
+#define ICON_CELL_XPAD 6
+
+typedef struct {
+       GtkScrolledWindow  parent;
+       GtkTreeView        *tree_view;
+       GtkCellRenderer    *eject_icon_cell_renderer;
+       char               *uri;
+       GtkListStore       *store;
+       GtkTreeModel       *filter_model;
+       NautilusWindow *window;
+       NautilusBookmarkList *bookmarks;
+       GVolumeMonitor *volume_monitor;
+
+       gboolean devices_header_added;
+       gboolean bookmarks_header_added;
+
+       /* DnD */
+       GList     *drag_list;
+       gboolean  drag_data_received;
+       int       drag_data_info;
+       gboolean  drop_occured;
+
+       GtkWidget *popup_menu;
+       GtkWidget *popup_menu_open_in_new_tab_item;
+       GtkWidget *popup_menu_add_shortcut_item;
+       GtkWidget *popup_menu_remove_item;
+       GtkWidget *popup_menu_rename_item;
+       GtkWidget *popup_menu_separator_item;
+       GtkWidget *popup_menu_mount_item;
+       GtkWidget *popup_menu_unmount_item;
+       GtkWidget *popup_menu_eject_item;
+       GtkWidget *popup_menu_rescan_item;
+       GtkWidget *popup_menu_empty_trash_item;
+       GtkWidget *popup_menu_start_item;
+       GtkWidget *popup_menu_stop_item;
+
+       /* volume mounting - delayed open process */
+       gboolean mounting;
+       NautilusWindowSlot *go_to_after_mount_slot;
+       NautilusWindowOpenFlags go_to_after_mount_flags;
+
+       GtkTreePath *eject_highlight_path;
+
+       guint bookmarks_changed_id;
+} NautilusPlacesSidebar;
+
+typedef struct {
+       GtkScrolledWindowClass parent;
+} NautilusPlacesSidebarClass;
+
+typedef struct {
+        GObject parent;
+} NautilusPlacesSidebarProvider;
+
+typedef struct {
+        GObjectClass parent;
+} NautilusPlacesSidebarProviderClass;
+
+enum {
+       PLACES_SIDEBAR_COLUMN_ROW_TYPE,
+       PLACES_SIDEBAR_COLUMN_URI,
+       PLACES_SIDEBAR_COLUMN_DRIVE,
+       PLACES_SIDEBAR_COLUMN_VOLUME,
+       PLACES_SIDEBAR_COLUMN_MOUNT,
+       PLACES_SIDEBAR_COLUMN_NAME,
+       PLACES_SIDEBAR_COLUMN_ICON,
+       PLACES_SIDEBAR_COLUMN_INDEX,
+       PLACES_SIDEBAR_COLUMN_EJECT,
+       PLACES_SIDEBAR_COLUMN_NO_EJECT,
+       PLACES_SIDEBAR_COLUMN_BOOKMARK,
+       PLACES_SIDEBAR_COLUMN_TOOLTIP,
+       PLACES_SIDEBAR_COLUMN_EJECT_ICON,
+       PLACES_SIDEBAR_COLUMN_SECTION_TYPE,
+       PLACES_SIDEBAR_COLUMN_HEADING_TEXT,
+
+       PLACES_SIDEBAR_COLUMN_COUNT
+};
+
+typedef enum {
+       PLACES_BUILT_IN,
+       PLACES_MOUNTED_VOLUME,
+       PLACES_BOOKMARK,
+       PLACES_HEADING,
+} PlaceType;
+
+typedef enum {
+       SECTION_DEVICES,
+       SECTION_BOOKMARKS,
+       SECTION_COMPUTER,
+       SECTION_NETWORK,
+} SectionType;
+
+static void  open_selected_bookmark                    (NautilusPlacesSidebar        *sidebar,
+                                                       GtkTreeModel                 *model,
+                                                       GtkTreeIter                  *iter,
+                                                       NautilusWindowOpenFlags flags);
+static void  nautilus_places_sidebar_style_set         (GtkWidget                    *widget,
+                                                       GtkStyle                     *previous_style);
+static gboolean eject_or_unmount_bookmark              (NautilusPlacesSidebar *sidebar,
+                                                       GtkTreePath *path);
+static gboolean eject_or_unmount_selection             (NautilusPlacesSidebar *sidebar);
+static void  check_unmount_and_eject                   (GMount *mount,
+                                                       GVolume *volume,
+                                                       GDrive *drive,
+                                                       gboolean *show_unmount,
+                                                       gboolean *show_eject);
+
+static void bookmarks_check_popup_sensitivity          (NautilusPlacesSidebar *sidebar);
+
+/* Identifiers for target types */
+enum {
+  GTK_TREE_MODEL_ROW,
+  TEXT_URI_LIST
+};
+
+/* Target types for dragging from the shortcuts list */
+static const GtkTargetEntry nautilus_shortcuts_source_targets[] = {
+       { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
+};
+
+/* Target types for dropping into the shortcuts list */
+static const GtkTargetEntry nautilus_shortcuts_drop_targets [] = {
+       { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
+       { "text/uri-list", 0, TEXT_URI_LIST }
+};
+
+/* Drag and drop interface declarations */
+typedef struct {
+  GtkTreeModelFilter parent;
+
+  NautilusPlacesSidebar *sidebar;
+} NautilusShortcutsModelFilter;
+
+typedef struct {
+  GtkTreeModelFilterClass parent_class;
+} NautilusShortcutsModelFilterClass;
+
+#define NAUTILUS_SHORTCUTS_MODEL_FILTER_TYPE (_nautilus_shortcuts_model_filter_get_type ())
+#define NAUTILUS_SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_SHORTCUTS_MODEL_FILTER_TYPE, NautilusShortcutsModelFilter))
+
+GType _nautilus_shortcuts_model_filter_get_type (void);
+static void nautilus_shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NautilusShortcutsModelFilter,
+                        _nautilus_shortcuts_model_filter,
+                        GTK_TYPE_TREE_MODEL_FILTER,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
+                                               nautilus_shortcuts_model_filter_drag_source_iface_init));
+
+static GtkTreeModel *nautilus_shortcuts_model_filter_new (NautilusPlacesSidebar *sidebar,
+                                                         GtkTreeModel          *child_model,
+                                                         GtkTreePath           *root);
+
+G_DEFINE_TYPE (NautilusPlacesSidebar, nautilus_places_sidebar, GTK_TYPE_SCROLLED_WINDOW);
+
+static GdkPixbuf *
+get_eject_icon (NautilusPlacesSidebar *sidebar,
+               gboolean highlighted)
+{
+       GdkPixbuf *eject;
+       GtkIconInfo *icon_info;
+       GIcon *icon;
+       int icon_size;
+       GtkIconTheme *icon_theme;
+       GtkStyleContext *style;
+
+       icon_theme = gtk_icon_theme_get_default ();
+       icon_size = nautilus_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU);
+       icon = g_themed_icon_new_with_default_fallbacks ("media-eject-symbolic");
+       icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme, icon, icon_size, 0);
+
+       style = gtk_widget_get_style_context (GTK_WIDGET (sidebar));
+       eject = gtk_icon_info_load_symbolic_for_context (icon_info,
+                                                        style,
+                                                        NULL,
+                                                        NULL);
+
+       if (highlighted) {
+               GdkPixbuf *high;
+               high = eel_create_spotlight_pixbuf (eject);
+               g_object_unref (eject);
+               eject = high;
+       }
+
+       g_object_unref (icon);
+       gtk_icon_info_free (icon_info);
+
+       return eject;
+}
+
+static gboolean
+is_built_in_bookmark (NautilusFile *file)
+{
+       gboolean built_in;
+       gint idx;
+
+       if (nautilus_file_is_home (file)) {
+               return TRUE;
+       }
+
+       built_in = FALSE;
+
+       for (idx = 0; idx < G_USER_N_DIRECTORIES; idx++) {
+               /* PUBLIC_SHARE and TEMPLATES are not in our built-in list */
+               if (nautilus_file_is_user_special_directory (file, idx)) {
+                       if (idx != G_USER_DIRECTORY_PUBLIC_SHARE &&  idx != G_USER_DIRECTORY_TEMPLATES) {
+                               built_in = TRUE;
+                       }
+
+                       break;
+               }
+       }
+
+       return built_in;
+}
+
+static GtkTreeIter
+add_heading (NautilusPlacesSidebar *sidebar,
+            SectionType section_type,
+            const gchar *title)
+{
+       GtkTreeIter iter, child_iter;
+
+       gtk_list_store_append (sidebar->store, &iter);
+       gtk_list_store_set (sidebar->store, &iter,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_HEADING,
+                           PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type,   
+                           PLACES_SIDEBAR_COLUMN_HEADING_TEXT, title,
+                           PLACES_SIDEBAR_COLUMN_EJECT, FALSE,
+                           PLACES_SIDEBAR_COLUMN_NO_EJECT, TRUE,
+                           -1);
+
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->filter_model));
+       gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (sidebar->filter_model),
+                                                         &child_iter,
+                                                         &iter);
+
+       return child_iter;
+}
+
+static void
+check_heading_for_section (NautilusPlacesSidebar *sidebar,
+                          SectionType section_type)
+{
+       switch (section_type) {
+       case SECTION_DEVICES:
+               if (!sidebar->devices_header_added) {
+                       add_heading (sidebar, SECTION_DEVICES,
+                                    _("Devices"));
+                       sidebar->devices_header_added = TRUE;
+               }
+
+               break;
+       case SECTION_BOOKMARKS:
+               if (!sidebar->bookmarks_header_added) {
+                       add_heading (sidebar, SECTION_BOOKMARKS,
+                                    _("Bookmarks"));
+                       sidebar->bookmarks_header_added = TRUE;
+               }
+
+               break;
+       default:
+               break;
+       }
+}
+
+static GtkTreeIter
+add_place (NautilusPlacesSidebar *sidebar,
+          PlaceType place_type,
+          SectionType section_type,
+          const char *name,
+          GIcon *icon,
+          const char *uri,
+          GDrive *drive,
+          GVolume *volume,
+          GMount *mount,
+          const int index,
+          const char *tooltip)
+{
+       GdkPixbuf            *pixbuf;
+       GtkTreeIter           iter, child_iter;
+       GdkPixbuf            *eject;
+       NautilusIconInfo *icon_info;
+       int icon_size;
+       gboolean show_eject, show_unmount;
+       gboolean show_eject_button;
+
+       check_heading_for_section (sidebar, section_type);
+
+       icon_size = nautilus_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU);
+       icon_info = nautilus_icon_info_lookup (icon, icon_size);
+
+       pixbuf = nautilus_icon_info_get_pixbuf_at_size (icon_info, icon_size);
+       g_object_unref (icon_info);
+
+       check_unmount_and_eject (mount, volume, drive,
+                                &show_unmount, &show_eject);
+
+       if (show_unmount || show_eject) {
+               g_assert (place_type != PLACES_BOOKMARK);
+       }
+
+       if (mount == NULL) {
+               show_eject_button = FALSE;
+       } else {
+               show_eject_button = (show_unmount || show_eject);
+       }
+
+       if (show_eject_button) {
+               eject = get_eject_icon (sidebar, FALSE);
+       } else {
+               eject = NULL;
+       }
+
+       gtk_list_store_append (sidebar->store, &iter);
+       gtk_list_store_set (sidebar->store, &iter,
+                           PLACES_SIDEBAR_COLUMN_ICON, pixbuf,
+                           PLACES_SIDEBAR_COLUMN_NAME, name,
+                           PLACES_SIDEBAR_COLUMN_URI, uri,
+                           PLACES_SIDEBAR_COLUMN_DRIVE, drive,
+                           PLACES_SIDEBAR_COLUMN_VOLUME, volume,
+                           PLACES_SIDEBAR_COLUMN_MOUNT, mount,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, place_type,
+                           PLACES_SIDEBAR_COLUMN_INDEX, index,
+                           PLACES_SIDEBAR_COLUMN_EJECT, show_eject_button,
+                           PLACES_SIDEBAR_COLUMN_NO_EJECT, !show_eject_button,
+                           PLACES_SIDEBAR_COLUMN_BOOKMARK, place_type != PLACES_BOOKMARK,
+                           PLACES_SIDEBAR_COLUMN_TOOLTIP, tooltip,
+                           PLACES_SIDEBAR_COLUMN_EJECT_ICON, eject,
+                           PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type,
+                           -1);
+
+       if (pixbuf != NULL) {
+               g_object_unref (pixbuf);
+       }
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->filter_model));
+       gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (sidebar->filter_model),
+                                                         &child_iter,
+                                                         &iter);
+       return child_iter;
+}
+
+static void
+compare_for_selection (NautilusPlacesSidebar *sidebar,
+                      const gchar *location,
+                      const gchar *added_uri,
+                      const gchar *last_uri,
+                      GtkTreeIter *iter,
+                      GtkTreePath **path)
+{
+       int res;
+
+       res = g_strcmp0 (added_uri, last_uri);
+
+       if (res == 0) {
+               /* last_uri always comes first */
+               if (*path != NULL) {
+                       gtk_tree_path_free (*path);
+               }
+               *path = gtk_tree_model_get_path (sidebar->filter_model,
+                                                iter);
+       } else if (g_strcmp0 (location, added_uri) == 0) {
+               if (*path == NULL) {
+                       *path = gtk_tree_model_get_path (sidebar->filter_model,
+                                                        iter);
+               }
+       }
+}
+
+static void
+update_places (NautilusPlacesSidebar *sidebar)
+{
+       NautilusBookmark *bookmark;
+       GtkTreeSelection *selection;
+       GtkTreeIter last_iter;
+       GtkTreePath *select_path;
+       GtkTreeModel *model;
+       GVolumeMonitor *volume_monitor;
+       GList *mounts, *l, *ll;
+       GMount *mount;
+       GList *drives;
+       GDrive *drive;
+       GList *volumes;
+       GVolume *volume;
+       int bookmark_count, index;
+       char *location, *mount_uri, *name, *desktop_path, *last_uri;
+       const gchar *path, *bookmark_name;
+       GIcon *icon;
+       GFile *root;
+       NautilusWindowSlot *slot;
+       char *tooltip;
+       GList *network_mounts;
+       NautilusFile *file;
+
+       model = NULL;
+       last_uri = NULL;
+       select_path = NULL;
+
+       selection = gtk_tree_view_get_selection (sidebar->tree_view);
+       if (gtk_tree_selection_get_selected (selection, &model, &last_iter)) {
+               gtk_tree_model_get (model,
+                                   &last_iter,
+                                   PLACES_SIDEBAR_COLUMN_URI, &last_uri, -1);
+       }
+       gtk_list_store_clear (sidebar->store);
+
+       sidebar->devices_header_added = FALSE;
+       sidebar->bookmarks_header_added = FALSE;
+
+       slot = nautilus_window_get_active_slot (sidebar->window);
+       location = nautilus_window_slot_get_current_uri (slot);
+
+       volume_monitor = sidebar->volume_monitor;
+
+       /* first go through all connected drives */
+       drives = g_volume_monitor_get_connected_drives (volume_monitor);
+
+       for (l = drives; l != NULL; l = l->next) {
+               drive = l->data;
+
+               volumes = g_drive_get_volumes (drive);
+               if (volumes != NULL) {
+                       for (ll = volumes; ll != NULL; ll = ll->next) {
+                               volume = ll->data;
+                               mount = g_volume_get_mount (volume);
+                               if (mount != NULL) {
+                                       /* Show mounted volume in the sidebar */
+                                       icon = g_mount_get_icon (mount);
+                                       root = g_mount_get_default_location (mount);
+                                       mount_uri = g_file_get_uri (root);
+                                       name = g_mount_get_name (mount);
+                                       tooltip = g_file_get_parse_name (root);
+
+                                       last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME,
+                                                              SECTION_DEVICES,
+                                                              name, icon, mount_uri,
+                                                              drive, volume, mount, 0, tooltip);
+                                       compare_for_selection (sidebar,
+                                                              location, mount_uri, last_uri,
+                                                              &last_iter, &select_path);
+                                       g_object_unref (root);
+                                       g_object_unref (mount);
+                                       g_object_unref (icon);
+                                       g_free (tooltip);
+                                       g_free (name);
+                                       g_free (mount_uri);
+                               } else {
+                                       /* Do show the unmounted volumes in the sidebar;
+                                        * this is so the user can mount it (in case automounting
+                                        * is off).
+                                        *
+                                        * Also, even if automounting is enabled, this gives a visual
+                                        * cue that the user should remember to yank out the media if
+                                        * he just unmounted it.
+                                        */
+                                       icon = g_volume_get_icon (volume);
+                                       name = g_volume_get_name (volume);
+                                       tooltip = g_strdup_printf (_("Mount and open %s"), name);
+
+                                       last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME,
+                                                              SECTION_DEVICES,
+                                                              name, icon, NULL,
+                                                              drive, volume, NULL, 0, tooltip);
+                                       g_object_unref (icon);
+                                       g_free (name);
+                                       g_free (tooltip);
+                               }
+                               g_object_unref (volume);
+                       }
+                       g_list_free (volumes);
+               } else {
+                       if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive)) {
+                               /* If the drive has no mountable volumes and we cannot detect media change.. we
+                                * display the drive in the sidebar so the user can manually poll the drive by
+                                * right clicking and selecting "Rescan..."
+                                *
+                                * This is mainly for drives like floppies where media detection doesn't
+                                * work.. but it's also for human beings who like to turn off media detection
+                                * in the OS to save battery juice.
+                                */
+                               icon = g_drive_get_icon (drive);
+                               name = g_drive_get_name (drive);
+                               tooltip = g_strdup_printf (_("Mount and open %s"), name);
+
+                               last_iter = add_place (sidebar, PLACES_BUILT_IN,
+                                                      SECTION_DEVICES,
+                                                      name, icon, NULL,
+                                                      drive, NULL, NULL, 0, tooltip);
+                               g_object_unref (icon);
+                               g_free (tooltip);
+                               g_free (name);
+                       }
+               }
+               g_object_unref (drive);
+       }
+       g_list_free (drives);
+
+       /* add all volumes that is not associated with a drive */
+       volumes = g_volume_monitor_get_volumes (volume_monitor);
+       for (l = volumes; l != NULL; l = l->next) {
+               volume = l->data;
+               drive = g_volume_get_drive (volume);
+               if (drive != NULL) {
+                       g_object_unref (volume);
+                       g_object_unref (drive);
+                       continue;
+               }
+               mount = g_volume_get_mount (volume);
+               if (mount != NULL) {
+                       icon = g_mount_get_icon (mount);
+                       root = g_mount_get_default_location (mount);
+                       mount_uri = g_file_get_uri (root);
+                       tooltip = g_file_get_parse_name (root);
+                       g_object_unref (root);
+                       name = g_mount_get_name (mount);
+                       last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME,
+                                              SECTION_DEVICES,
+                                              name, icon, mount_uri,
+                                              NULL, volume, mount, 0, tooltip);
+                       compare_for_selection (sidebar,
+                                              location, mount_uri, last_uri,
+                                              &last_iter, &select_path);
+                       g_object_unref (mount);
+                       g_object_unref (icon);
+                       g_free (name);
+                       g_free (tooltip);
+                       g_free (mount_uri);
+               } else {
+                       /* see comment above in why we add an icon for an unmounted mountable volume */
+                       icon = g_volume_get_icon (volume);
+                       name = g_volume_get_name (volume);
+                       last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME,
+                                              SECTION_DEVICES,
+                                              name, icon, NULL,
+                                              NULL, volume, NULL, 0, name);
+                       g_object_unref (icon);
+                       g_free (name);
+               }
+               g_object_unref (volume);
+       }
+       g_list_free (volumes);
+       
+       /* add bookmarks */
+       bookmark_count = nautilus_bookmark_list_length (sidebar->bookmarks);
+
+       for (index = 0; index < bookmark_count; ++index) {
+               bookmark = nautilus_bookmark_list_item_at (sidebar->bookmarks, index);
+
+               if (nautilus_bookmark_uri_known_not_to_exist (bookmark)) {
+                       continue;
+               }
+
+               root = nautilus_bookmark_get_location (bookmark);
+               file = nautilus_file_get (root);
+
+               if (is_built_in_bookmark (file)) {
+                       g_object_unref (root);
+                       nautilus_file_unref (file);
+                       continue;
+               }
+               nautilus_file_unref (file);
+
+               bookmark_name = nautilus_bookmark_get_name (bookmark);
+               icon = nautilus_bookmark_get_icon (bookmark);
+               mount_uri = nautilus_bookmark_get_uri (bookmark);
+               tooltip = g_file_get_parse_name (root);
+
+               last_iter = add_place (sidebar, PLACES_BOOKMARK,
+                                      SECTION_BOOKMARKS,
+                                      bookmark_name, icon, mount_uri,
+                                      NULL, NULL, NULL, index,
+                                      tooltip);
+               compare_for_selection (sidebar,
+                                      location, mount_uri, last_uri,
+                                      &last_iter, &select_path);
+
+               g_object_unref (root);
+               g_object_unref (icon);
+               g_free (mount_uri);
+               g_free (tooltip);
+       }
+
+       last_iter = add_heading (sidebar, SECTION_COMPUTER,
+                              _("Computer"));
+
+       /* add built in bookmarks */
+
+       /* home folder */
+       mount_uri = nautilus_get_home_directory_uri ();
+       icon = g_themed_icon_new (NAUTILUS_ICON_HOME);
+       last_iter = add_place (sidebar, PLACES_BUILT_IN,
+                              SECTION_COMPUTER,
+                              _("Home"), icon,
+                              mount_uri, NULL, NULL, NULL, 0,
+                              _("Open your personal folder"));
+       g_object_unref (icon);
+       compare_for_selection (sidebar,
+                              location, mount_uri, last_uri,
+                              &last_iter, &select_path);
+       g_free (mount_uri);
+
+       if (g_settings_get_boolean (gnome_background_preferences, NAUTILUS_PREFERENCES_SHOW_DESKTOP) &&
+           !g_settings_get_boolean (nautilus_preferences, NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR)) {
+               /* desktop */
+               desktop_path = nautilus_get_desktop_directory ();
+               mount_uri = g_filename_to_uri (desktop_path, NULL, NULL);
+               icon = g_themed_icon_new (NAUTILUS_ICON_DESKTOP);
+               last_iter = add_place (sidebar, PLACES_BUILT_IN,
+                                      SECTION_COMPUTER,
+                                      _("Desktop"), icon,
+                                      mount_uri, NULL, NULL, NULL, 0,
+                                      _("Open the contents of your desktop in a folder"));
+               g_object_unref (icon);
+               compare_for_selection (sidebar,
+                                      location, mount_uri, last_uri,
+                                      &last_iter, &select_path);
+               g_free (mount_uri);
+               g_free (desktop_path);
+       }
+
+       
+       /* XDG directories */
+       for (index = 0; index < G_USER_N_DIRECTORIES; index++) {
+
+               if (index == G_USER_DIRECTORY_DESKTOP ||
+                   index == G_USER_DIRECTORY_TEMPLATES ||
+                   index == G_USER_DIRECTORY_PUBLIC_SHARE) {
+                       continue;
+               }
+
+               path = g_get_user_special_dir (index);
+
+               /* xdg resets special dirs to the home directory in case
+                * it's not finiding what it expects. We don't want the home
+                * to be added multiple times in that weird configuration.
+                */
+               if (!path || g_strcmp0 (path, g_get_home_dir ()) == 0) {
+                       continue;
+               }
+
+               root = g_file_new_for_path (path);
+               name = g_file_get_basename (root);
+               icon = nautilus_user_special_directory_get_gicon (index);
+               mount_uri = g_file_get_uri (root);
+               tooltip = g_file_get_parse_name (root);
+
+               last_iter = add_place (sidebar, PLACES_BUILT_IN,
+                                      SECTION_COMPUTER,
+                                      name, icon, mount_uri,
+                                      NULL, NULL, NULL, 0,
+                                      tooltip);
+               compare_for_selection (sidebar,
+                                      location, mount_uri, last_uri,
+                                      &last_iter, &select_path);
+               g_free (name);
+               g_object_unref (root);
+               g_object_unref (icon);
+               g_free (mount_uri);
+               g_free (tooltip);
+       }
+
+       /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
+       network_mounts = NULL;
+       mounts = g_volume_monitor_get_mounts (volume_monitor);
+
+       for (l = mounts; l != NULL; l = l->next) {
+               mount = l->data;
+               if (g_mount_is_shadowed (mount)) {
+                       g_object_unref (mount);
+                       continue;
+               }
+               volume = g_mount_get_volume (mount);
+               if (volume != NULL) {
+                       g_object_unref (volume);
+                       g_object_unref (mount);
+                       continue;
+               }
+               root = g_mount_get_default_location (mount);
+
+               if (!g_file_is_native (root)) {
+                       network_mounts = g_list_prepend (network_mounts, g_object_ref (mount));
+                       continue;
+               }
+
+               icon = g_mount_get_icon (mount);
+               mount_uri = g_file_get_uri (root);
+               name = g_mount_get_name (mount);
+               tooltip = g_file_get_parse_name (root);
+               last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME,
+                                      SECTION_COMPUTER,
+                                      name, icon, mount_uri,
+                                      NULL, NULL, mount, 0, tooltip);
+               compare_for_selection (sidebar,
+                                      location, mount_uri, last_uri,
+                                      &last_iter, &select_path);
+               g_object_unref (root);
+               g_object_unref (mount);
+               g_object_unref (icon);
+               g_free (name);
+               g_free (mount_uri);
+               g_free (tooltip);
+       }
+       g_list_free (mounts);
+
+       /* file system root */
+       mount_uri = "file:///"; /* No need to strdup */
+       icon = g_themed_icon_new (NAUTILUS_ICON_FILESYSTEM);
+       last_iter = add_place (sidebar, PLACES_BUILT_IN,
+                              SECTION_COMPUTER,
+                              _("File System"), icon,
+                              mount_uri, NULL, NULL, NULL, 0,
+                              _("Open the contents of the File System"));
+       g_object_unref (icon);
+       compare_for_selection (sidebar,
+                              location, mount_uri, last_uri,
+                              &last_iter, &select_path);
+
+       mount_uri = "trash:///"; /* No need to strdup */
+       icon = nautilus_trash_monitor_get_icon ();
+       last_iter = add_place (sidebar, PLACES_BUILT_IN,
+                              SECTION_COMPUTER,
+                              _("Trash"), icon, mount_uri,
+                              NULL, NULL, NULL, 0,
+                              _("Open the trash"));
+       compare_for_selection (sidebar,
+                              location, mount_uri, last_uri,
+                              &last_iter, &select_path);
+       g_object_unref (icon);
+
+       /* network */
+       last_iter = add_heading (sidebar, SECTION_NETWORK,
+                                _("Network"));
+
+       network_mounts = g_list_reverse (network_mounts);
+       for (l = network_mounts; l != NULL; l = l->next) {
+               mount = l->data;
+               root = g_mount_get_default_location (mount);
+               icon = g_mount_get_icon (mount);
+               mount_uri = g_file_get_uri (root);
+               name = g_mount_get_name (mount);
+               tooltip = g_file_get_parse_name (root);
+               last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME,
+                                      SECTION_NETWORK,
+                                      name, icon, mount_uri,
+                                      NULL, NULL, mount, 0, tooltip);
+               compare_for_selection (sidebar,
+                                      location, mount_uri, last_uri,
+                                      &last_iter, &select_path);
+               g_object_unref (root);
+               g_object_unref (mount);
+               g_object_unref (icon);
+               g_free (name);
+               g_free (mount_uri);
+               g_free (tooltip);
+       }
+
+       g_list_free_full (network_mounts, g_object_unref);
+
+       /* network:// */
+       mount_uri = "network:///"; /* No need to strdup */
+       icon = g_themed_icon_new (NAUTILUS_ICON_NETWORK);
+       last_iter = add_place (sidebar, PLACES_BUILT_IN,
+                              SECTION_NETWORK,
+                              _("Browse Network"), icon,
+                              mount_uri, NULL, NULL, NULL, 0,
+                              _("Browse the contents of the network"));
+       g_object_unref (icon);
+       compare_for_selection (sidebar,
+                              location, mount_uri, last_uri,
+                              &last_iter, &select_path);
+       
+       g_free (location);
+
+       if (select_path != NULL) {
+               gtk_tree_selection_select_path (selection, select_path);
+       }
+
+       if (select_path != NULL) {
+               gtk_tree_path_free (select_path);
+       }
+
+       g_free (last_uri);
+}
+
+static void
+mount_added_callback (GVolumeMonitor *volume_monitor,
+                     GMount *mount,
+                     NautilusPlacesSidebar *sidebar)
+{
+       update_places (sidebar);
+}
+
+static void
+mount_removed_callback (GVolumeMonitor *volume_monitor,
+                       GMount *mount,
+                       NautilusPlacesSidebar *sidebar)
+{
+       update_places (sidebar);
+}
+
+static void
+mount_changed_callback (GVolumeMonitor *volume_monitor,
+                       GMount *mount,
+                       NautilusPlacesSidebar *sidebar)
+{
+       update_places (sidebar);
+}
+
+static void
+volume_added_callback (GVolumeMonitor *volume_monitor,
+                      GVolume *volume,
+                      NautilusPlacesSidebar *sidebar)
+{
+       update_places (sidebar);
+}
+
+static void
+volume_removed_callback (GVolumeMonitor *volume_monitor,
+                        GVolume *volume,
+                        NautilusPlacesSidebar *sidebar)
+{
+       update_places (sidebar);
+}
+
+static void
+volume_changed_callback (GVolumeMonitor *volume_monitor,
+                        GVolume *volume,
+                        NautilusPlacesSidebar *sidebar)
+{
+       update_places (sidebar);
+}
+
+static void
+drive_disconnected_callback (GVolumeMonitor *volume_monitor,
+                            GDrive         *drive,
+                            NautilusPlacesSidebar *sidebar)
+{
+       update_places (sidebar);
+}
+
+static void
+drive_connected_callback (GVolumeMonitor *volume_monitor,
+                         GDrive         *drive,
+                         NautilusPlacesSidebar *sidebar)
+{
+       update_places (sidebar);
+}
+
+static void
+drive_changed_callback (GVolumeMonitor *volume_monitor,
+                       GDrive         *drive,
+                       NautilusPlacesSidebar *sidebar)
+{
+       update_places (sidebar);
+}
+
+static gboolean
+over_eject_button (NautilusPlacesSidebar *sidebar,
+                  gint x,
+                  gint y,
+                  GtkTreePath **path)
+{
+       GtkTreeViewColumn *column;
+       int width, x_offset, hseparator;
+       int eject_button_size;
+       gboolean show_eject;
+       GtkTreeIter iter;
+       GtkTreeModel *model;
+
+       *path = NULL;
+       model = gtk_tree_view_get_model (sidebar->tree_view);
+
+       if (gtk_tree_view_get_path_at_pos (sidebar->tree_view,
+                                          x, y,
+                                          path, &column, NULL, NULL)) {
+
+               gtk_tree_model_get_iter (model, &iter, *path);
+               gtk_tree_model_get (model, &iter,
+                                   PLACES_SIDEBAR_COLUMN_EJECT, &show_eject,
+                                   -1);
+
+               if (!show_eject) {
+                       goto out;
+               }
+
+
+               gtk_widget_style_get (GTK_WIDGET (sidebar->tree_view),
+                                     "horizontal-separator", &hseparator,
+                                     NULL);
+
+               /* Reload cell attributes for this particular row */
+               gtk_tree_view_column_cell_set_cell_data (column,
+                                                        model, &iter, FALSE, FALSE);
+
+               gtk_tree_view_column_cell_get_position (column,
+                                                       sidebar->eject_icon_cell_renderer,
+                                                       &x_offset, &width);
+
+               eject_button_size = nautilus_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU);
+
+               /* This is kinda weird, but we have to do it to workaround gtk+ expanding
+                * the eject cell renderer (even thought we told it not to) and we then
+                * had to set it right-aligned */
+               x_offset += width - hseparator - EJECT_BUTTON_XPAD - eject_button_size;
+
+               if (x - x_offset >= 0 &&
+                   x - x_offset <= eject_button_size) {
+                       return TRUE;
+               }
+       }
+
+ out:
+       if (*path != NULL) {
+               gtk_tree_path_free (*path);
+               *path = NULL;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+clicked_eject_button (NautilusPlacesSidebar *sidebar,
+                     GtkTreePath **path)
+{
+       GdkEvent *event = gtk_get_current_event ();
+       GdkEventButton *button_event = (GdkEventButton *) event;
+
+       if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
+            over_eject_button (sidebar, button_event->x, button_event->y, path)) {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+desktop_setting_changed_callback (gpointer user_data)
+{
+       NautilusPlacesSidebar *sidebar;
+
+       sidebar = NAUTILUS_PLACES_SIDEBAR (user_data);
+
+       update_places (sidebar);
+}
+
+static void
+loading_uri_callback (NautilusWindow *window,
+                     char *location,
+                     NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeSelection *selection;
+       GtkTreeIter      iter;
+       gboolean         valid;
+       char             *uri;
+
+        if (strcmp (sidebar->uri, location) != 0) {
+               g_free (sidebar->uri);
+                sidebar->uri = g_strdup (location);
+  
+               /* set selection if any place matches location */
+               selection = gtk_tree_view_get_selection (sidebar->tree_view);
+               gtk_tree_selection_unselect_all (selection);
+               valid = gtk_tree_model_get_iter_first (sidebar->filter_model, &iter);
+
+               while (valid) {
+                       gtk_tree_model_get (sidebar->filter_model, &iter, 
+                                           PLACES_SIDEBAR_COLUMN_URI, &uri,
+                                           -1);
+                       if (uri != NULL) {
+                               if (strcmp (uri, location) == 0) {
+                                       g_free (uri);
+                                       gtk_tree_selection_select_iter (selection, &iter);
+                                       break;
+                               }
+                               g_free (uri);
+                       }
+                       valid = gtk_tree_model_iter_next (sidebar->filter_model, &iter);
+               }
+       }
+}
+
+/* Computes the appropriate row and position for dropping */
+static gboolean
+compute_drop_position (GtkTreeView *tree_view,
+                      int                      x,
+                      int                      y,
+                      GtkTreePath            **path,
+                      GtkTreeViewDropPosition *pos,
+                      NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       PlaceType place_type;
+       SectionType section_type;
+
+       if (!gtk_tree_view_get_dest_row_at_pos (tree_view,
+                                          x,
+                                          y,
+                                          path,
+                                          pos)) {
+               return FALSE;
+       }
+
+       model = gtk_tree_view_get_model (tree_view);
+
+       gtk_tree_model_get_iter (model, &iter, *path);
+       gtk_tree_model_get (model, &iter,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
+                           PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &section_type,
+                           -1);
+
+       if (place_type == PLACES_HEADING && section_type != SECTION_BOOKMARKS) {
+               /* never drop on headings, but special case the bookmarks heading,
+                * so we can drop bookmarks in between it and the first item.
+                */
+
+               gtk_tree_path_free (*path);
+               *path = NULL;
+               
+               return FALSE;
+       }
+
+       if (section_type != SECTION_BOOKMARKS &&
+           sidebar->drag_data_received &&
+           sidebar->drag_data_info == GTK_TREE_MODEL_ROW) {
+               /* don't allow dropping bookmarks into non-bookmark areas */
+
+               gtk_tree_path_free (*path);
+               *path = NULL;
+
+               return FALSE;
+       }
+
+       if (section_type == SECTION_BOOKMARKS) {
+               *pos = GTK_TREE_VIEW_DROP_AFTER;
+       } else {
+               /* non-bookmark shortcuts can only be dragged into */
+               *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
+       }
+
+       if (*pos != GTK_TREE_VIEW_DROP_BEFORE &&
+           sidebar->drag_data_received &&
+           sidebar->drag_data_info == GTK_TREE_MODEL_ROW) {
+               /* bookmark rows are never dragged into other bookmark rows */
+               *pos = GTK_TREE_VIEW_DROP_AFTER;
+       }
+
+       return TRUE;
+}
+
+static gboolean
+get_drag_data (GtkTreeView *tree_view,
+              GdkDragContext *context, 
+              unsigned int time)
+{
+       GdkAtom target;
+
+       target = gtk_drag_dest_find_target (GTK_WIDGET (tree_view), 
+                                           context, 
+                                           NULL);
+
+       if (target == GDK_NONE) {
+               return FALSE;
+       }
+
+       gtk_drag_get_data (GTK_WIDGET (tree_view),
+                          context, target, time);
+
+       return TRUE;
+}
+
+static void
+free_drag_data (NautilusPlacesSidebar *sidebar)
+{
+       sidebar->drag_data_received = FALSE;
+
+       if (sidebar->drag_list) {
+               nautilus_drag_destroy_selection_list (sidebar->drag_list);
+               sidebar->drag_list = NULL;
+       }
+}
+
+static gboolean
+can_accept_file_as_bookmark (NautilusFile *file)
+{
+       return (nautilus_file_is_directory (file) &&
+               !is_built_in_bookmark (file));
+}
+
+static gboolean
+can_accept_items_as_bookmarks (const GList *items)
+{
+       int max;
+       char *uri;
+       NautilusFile *file;
+
+       /* Iterate through selection checking if item will get accepted as a bookmark.
+        * If more than 100 items selected, return an over-optimistic result.
+        */
+       for (max = 100; items != NULL && max >= 0; items = items->next, max--) {
+               uri = ((NautilusDragSelectionItem *)items->data)->uri;
+               file = nautilus_file_get_by_uri (uri);
+               if (!can_accept_file_as_bookmark (file)) {
+                       nautilus_file_unref (file);
+                       return FALSE;
+               }
+               nautilus_file_unref (file);
+       }
+       
+       return TRUE;
+}
+
+static gboolean
+drag_motion_callback (GtkTreeView *tree_view,
+                     GdkDragContext *context,
+                     int x,
+                     int y,
+                     unsigned int time,
+                     NautilusPlacesSidebar *sidebar)
+{
+       GtkTreePath *path;
+       GtkTreeViewDropPosition pos;
+       int action;
+       GtkTreeIter iter;
+       char *uri;
+       gboolean res;
+
+       if (!sidebar->drag_data_received) {
+               if (!get_drag_data (tree_view, context, time)) {
+                       return FALSE;
+               }
+       }
+
+       path = NULL;
+       res = compute_drop_position (tree_view, x, y, &path, &pos, sidebar);
+
+       if (!res) {
+               goto out;
+       }
+
+       if (pos == GTK_TREE_VIEW_DROP_BEFORE ||
+           pos == GTK_TREE_VIEW_DROP_AFTER ) {
+               if (sidebar->drag_data_received &&
+                   sidebar->drag_data_info == GTK_TREE_MODEL_ROW) {
+                       action = GDK_ACTION_MOVE;
+               } else if (can_accept_items_as_bookmarks (sidebar->drag_list)) {
+                       action = GDK_ACTION_COPY;
+               } else {
+                       action = 0;
+               }
+       } else {
+               if (sidebar->drag_list == NULL) {
+                       action = 0;
+               } else {
+                       gtk_tree_model_get_iter (sidebar->filter_model,
+                                                &iter, path);
+                       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model),
+                                           &iter,
+                                           PLACES_SIDEBAR_COLUMN_URI, &uri,
+                                           -1);
+                       nautilus_drag_default_drop_action_for_icons (context, uri,
+                                                                    sidebar->drag_list,
+                                                                    &action);
+                       g_free (uri);
+               }
+       }
+
+       if (action != 0) {
+               gtk_tree_view_set_drag_dest_row (tree_view, path, pos);
+       }
+
+       if (path != NULL) {
+               gtk_tree_path_free (path);
+       }
+
+ out:
+       g_signal_stop_emission_by_name (tree_view, "drag-motion");
+
+       if (action != 0) {
+               gdk_drag_status (context, action, time);
+       } else {
+               gdk_drag_status (context, 0, time);
+       }
+
+       return TRUE;
+}
+
+static void
+drag_leave_callback (GtkTreeView *tree_view,
+                    GdkDragContext *context,
+                    unsigned int time,
+                    NautilusPlacesSidebar *sidebar)
+{
+       free_drag_data (sidebar);
+       gtk_tree_view_set_drag_dest_row (tree_view, NULL, GTK_TREE_VIEW_DROP_BEFORE);
+       g_signal_stop_emission_by_name (tree_view, "drag-leave");
+}
+
+/* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
+static void
+bookmarks_drop_uris (NautilusPlacesSidebar *sidebar,
+                    GtkSelectionData      *selection_data,
+                    int                    position)
+{
+       NautilusBookmark *bookmark;
+       NautilusFile *file;
+       char *uri;
+       char **uris;
+       int i;
+       GFile *location;
+       
+       uris = gtk_selection_data_get_uris (selection_data);
+       if (!uris)
+               return;
+       
+       for (i = 0; uris[i]; i++) {
+               uri = uris[i];
+               file = nautilus_file_get_by_uri (uri);
+
+               if (!can_accept_file_as_bookmark (file)) {
+                       nautilus_file_unref (file);
+                       continue;
+               }
+
+               uri = nautilus_file_get_drop_target_uri (file);
+               location = g_file_new_for_uri (uri);
+               nautilus_file_unref (file);
+
+               bookmark = nautilus_bookmark_new (location, NULL, NULL);
+
+               if (!nautilus_bookmark_list_contains (sidebar->bookmarks, bookmark)) {
+                       nautilus_bookmark_list_insert_item (sidebar->bookmarks, bookmark, position++);
+               }
+
+               g_object_unref (location);
+               g_object_unref (bookmark);
+               g_free (uri);
+       }
+
+       g_strfreev (uris);
+}
+
+static GList *
+uri_list_from_selection (GList *selection)
+{
+       NautilusDragSelectionItem *item;
+       GList *ret;
+       GList *l;
+       
+       ret = NULL;
+       for (l = selection; l != NULL; l = l->next) {
+               item = l->data;
+               ret = g_list_prepend (ret, item->uri);
+       }
+       
+       return g_list_reverse (ret);
+}
+
+static GList*
+build_selection_list (const char *data)
+{
+       NautilusDragSelectionItem *item;
+       GList *result;
+       char **uris;
+       char *uri;
+       int i;
+
+       uris = g_uri_list_extract_uris (data);
+
+       result = NULL;
+       for (i = 0; uris[i]; i++) {
+               uri = uris[i];
+               item = nautilus_drag_selection_item_new ();
+               item->uri = g_strdup (uri);
+               item->got_icon_position = FALSE;
+               result = g_list_prepend (result, item);
+       }
+
+       g_strfreev (uris);
+
+       return g_list_reverse (result);
+}
+
+static gboolean
+get_selected_iter (NautilusPlacesSidebar *sidebar,
+                  GtkTreeIter *iter)
+{
+       GtkTreeSelection *selection;
+
+       selection = gtk_tree_view_get_selection (sidebar->tree_view);
+
+       return gtk_tree_selection_get_selected (selection, NULL, iter);
+}
+
+/* Reorders the selected bookmark to the specified position */
+static void
+reorder_bookmarks (NautilusPlacesSidebar *sidebar,
+                  int                   new_position)
+{
+       GtkTreeIter iter;
+       PlaceType type; 
+       int old_position;
+
+       /* Get the selected path */
+
+       if (!get_selected_iter (sidebar, &iter))
+               g_assert_not_reached ();
+
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
+                           PLACES_SIDEBAR_COLUMN_INDEX, &old_position,
+                           -1);
+
+       if (type != PLACES_BOOKMARK ||
+           old_position < 0 ||
+           old_position >= nautilus_bookmark_list_length (sidebar->bookmarks)) {
+               return;
+       }
+
+       nautilus_bookmark_list_move_item (sidebar->bookmarks, old_position,
+                                         new_position);
+}
+
+static void
+drag_data_received_callback (GtkWidget *widget,
+                            GdkDragContext *context,
+                            int x,
+                            int y,
+                            GtkSelectionData *selection_data,
+                            unsigned int info,
+                            unsigned int time,
+                            NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeView *tree_view;
+       GtkTreePath *tree_path;
+       GtkTreeViewDropPosition tree_pos;
+       GtkTreeIter iter;
+       int position;
+       GtkTreeModel *model;
+       char *drop_uri;
+       GList *selection_list, *uris;
+       PlaceType place_type;
+       SectionType section_type;
+       gboolean success;
+
+       tree_view = GTK_TREE_VIEW (widget);
+
+       if (!sidebar->drag_data_received) {
+               if (gtk_selection_data_get_target (selection_data) != GDK_NONE &&
+                   info == TEXT_URI_LIST) {
+                       sidebar->drag_list = build_selection_list (gtk_selection_data_get_data (selection_data));
+               } else {
+                       sidebar->drag_list = NULL;
+               }
+               sidebar->drag_data_received = TRUE;
+               sidebar->drag_data_info = info;
+       }
+
+       g_signal_stop_emission_by_name (widget, "drag-data-received");
+
+       if (!sidebar->drop_occured) {
+               return;
+       }
+
+       /* Compute position */
+       compute_drop_position (tree_view, x, y, &tree_path, &tree_pos, sidebar);
+
+       success = FALSE;
+
+       if (tree_pos == GTK_TREE_VIEW_DROP_BEFORE ||
+           tree_pos == GTK_TREE_VIEW_DROP_AFTER) {
+               model = gtk_tree_view_get_model (tree_view);
+
+               if (!gtk_tree_model_get_iter (model, &iter, tree_path)) {
+                       goto out;
+               }
+
+               gtk_tree_model_get (model, &iter,
+                                   PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &section_type,
+                                   PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
+                                   PLACES_SIDEBAR_COLUMN_INDEX, &position,
+                                   -1);
+
+               if (section_type != SECTION_BOOKMARKS) {
+                       goto out;
+               }
+
+               if (tree_pos == GTK_TREE_VIEW_DROP_AFTER && place_type != PLACES_HEADING) {
+                       /* heading already has position 0 */
+                       position++;
+               }
+
+               switch (info) {
+               case TEXT_URI_LIST:
+                       bookmarks_drop_uris (sidebar, selection_data, position);
+                       success = TRUE;
+                       break;
+               case GTK_TREE_MODEL_ROW:
+                       reorder_bookmarks (sidebar, position);
+                       success = TRUE;
+                       break;
+               default:
+                       g_assert_not_reached ();
+                       break;
+               }
+       } else {
+               GdkDragAction real_action;
+
+               /* file transfer requested */
+               real_action = gdk_drag_context_get_selected_action (context);
+
+               if (real_action == GDK_ACTION_ASK) {
+                       real_action =
+                               nautilus_drag_drop_action_ask (GTK_WIDGET (tree_view),
+                                                              gdk_drag_context_get_actions (context));
+               }
+
+               if (real_action > 0) {
+                       model = gtk_tree_view_get_model (tree_view);
+
+                       gtk_tree_model_get_iter (model, &iter, tree_path);
+                       gtk_tree_model_get (model, &iter,
+                                           PLACES_SIDEBAR_COLUMN_URI, &drop_uri,
+                                           -1);
+
+                       switch (info) {
+                       case TEXT_URI_LIST:
+                               selection_list = build_selection_list (gtk_selection_data_get_data (selection_data));
+                               uris = uri_list_from_selection (selection_list);
+                               nautilus_file_operations_copy_move (uris, NULL, drop_uri,
+                                                                   real_action, GTK_WIDGET (tree_view),
+                                                                   NULL, NULL);
+                               nautilus_drag_destroy_selection_list (selection_list);
+                               g_list_free (uris);
+                               success = TRUE;
+                               break;
+                       case GTK_TREE_MODEL_ROW:
+                               success = FALSE;
+                               break;
+                       default:
+                               g_assert_not_reached ();
+                               break;
+                       }
+
+                       g_free (drop_uri);
+               }
+       }
+
+out:
+       sidebar->drop_occured = FALSE;
+       free_drag_data (sidebar);
+       gtk_drag_finish (context, success, FALSE, time);
+
+       gtk_tree_path_free (tree_path);
+}
+
+static gboolean
+drag_drop_callback (GtkTreeView *tree_view,
+                   GdkDragContext *context,
+                   int x,
+                   int y,
+                   unsigned int time,
+                   NautilusPlacesSidebar *sidebar)
+{
+       gboolean retval = FALSE;
+       sidebar->drop_occured = TRUE;
+       retval = get_drag_data (tree_view, context, time);
+       g_signal_stop_emission_by_name (tree_view, "drag-drop");
+       return retval;
+}
+
+/* Callback used when the file list's popup menu is detached */
+static void
+bookmarks_popup_menu_detach_cb (GtkWidget *attach_widget,
+                               GtkMenu   *menu)
+{
+       NautilusPlacesSidebar *sidebar;
+       
+       sidebar = NAUTILUS_PLACES_SIDEBAR (attach_widget);
+       g_assert (NAUTILUS_IS_PLACES_SIDEBAR (sidebar));
+       
+       sidebar->popup_menu = NULL;
+       sidebar->popup_menu_add_shortcut_item = NULL;
+       sidebar->popup_menu_remove_item = NULL;
+       sidebar->popup_menu_rename_item = NULL;
+       sidebar->popup_menu_separator_item = NULL;
+       sidebar->popup_menu_mount_item = NULL;
+       sidebar->popup_menu_unmount_item = NULL;
+       sidebar->popup_menu_eject_item = NULL;
+       sidebar->popup_menu_rescan_item = NULL;
+       sidebar->popup_menu_start_item = NULL;
+       sidebar->popup_menu_stop_item = NULL;
+       sidebar->popup_menu_empty_trash_item = NULL;
+}
+
+static void
+check_unmount_and_eject (GMount *mount,
+                        GVolume *volume,
+                        GDrive *drive,
+                        gboolean *show_unmount,
+                        gboolean *show_eject)
+{
+       *show_unmount = FALSE;
+       *show_eject = FALSE;
+
+       if (drive != NULL) {
+               *show_eject = g_drive_can_eject (drive);
+       }
+
+       if (volume != NULL) {
+               *show_eject |= g_volume_can_eject (volume);
+       }
+       if (mount != NULL) {
+               *show_eject |= g_mount_can_eject (mount);
+               *show_unmount = g_mount_can_unmount (mount) && !*show_eject;
+       }
+}
+
+static void
+check_visibility (GMount           *mount,
+                 GVolume          *volume,
+                 GDrive           *drive,
+                 gboolean         *show_mount,
+                 gboolean         *show_unmount,
+                 gboolean         *show_eject,
+                 gboolean         *show_rescan,
+                 gboolean         *show_start,
+                 gboolean         *show_stop)
+{
+       *show_mount = FALSE;
+       *show_rescan = FALSE;
+       *show_start = FALSE;
+       *show_stop = FALSE;
+
+       check_unmount_and_eject (mount, volume, drive, show_unmount, show_eject);
+
+       if (drive != NULL) {
+               if (g_drive_is_media_removable (drive) &&
+                   !g_drive_is_media_check_automatic (drive) && 
+                   g_drive_can_poll_for_media (drive))
+                       *show_rescan = TRUE;
+
+               *show_start = g_drive_can_start (drive) || g_drive_can_start_degraded (drive);
+               *show_stop  = g_drive_can_stop (drive);
+
+               if (*show_stop)
+                       *show_unmount = FALSE;
+       }
+
+       if (volume != NULL) {
+               if (mount == NULL)
+                       *show_mount = g_volume_can_mount (volume);
+       }
+}
+
+static void
+bookmarks_check_popup_sensitivity (NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       PlaceType type; 
+       GDrive *drive = NULL;
+       GVolume *volume = NULL;
+       GMount *mount = NULL;
+       gboolean show_mount;
+       gboolean show_unmount;
+       gboolean show_eject;
+       gboolean show_rescan;
+       gboolean show_start;
+       gboolean show_stop;
+       gboolean show_empty_trash;
+       char *uri = NULL;
+       
+       type = PLACES_BUILT_IN;
+
+       if (sidebar->popup_menu == NULL) {
+               return;
+       }
+
+       if (get_selected_iter (sidebar, &iter)) {
+               gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                                   PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
+                                   PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
+                                   PLACES_SIDEBAR_COLUMN_VOLUME, &volume,
+                                   PLACES_SIDEBAR_COLUMN_MOUNT, &mount,
+                                   PLACES_SIDEBAR_COLUMN_URI, &uri,
+                                   -1);
+       }
+
+       gtk_widget_set_visible (sidebar->popup_menu_add_shortcut_item, (type == PLACES_MOUNTED_VOLUME));
+
+       gtk_widget_set_sensitive (sidebar->popup_menu_remove_item, (type == PLACES_BOOKMARK));
+       gtk_widget_set_sensitive (sidebar->popup_menu_rename_item, (type == PLACES_BOOKMARK));
+       gtk_widget_set_sensitive (sidebar->popup_menu_empty_trash_item, !nautilus_trash_monitor_is_empty ());
+
+       check_visibility (mount, volume, drive,
+                         &show_mount, &show_unmount, &show_eject, &show_rescan, &show_start, &show_stop);
+
+       /* We actually want both eject and unmount since eject will unmount all volumes. 
+        * TODO: hide unmount if the drive only has a single mountable volume 
+        */
+
+       show_empty_trash = (uri != NULL) &&
+                          (!strcmp (uri, "trash:///"));
+
+       gtk_widget_set_visible (sidebar->popup_menu_separator_item,
+                     show_mount || show_unmount || show_eject || show_empty_trash);
+       gtk_widget_set_visible (sidebar->popup_menu_mount_item, show_mount);
+       gtk_widget_set_visible (sidebar->popup_menu_unmount_item, show_unmount);
+       gtk_widget_set_visible (sidebar->popup_menu_eject_item, show_eject);
+       gtk_widget_set_visible (sidebar->popup_menu_rescan_item, show_rescan);
+       gtk_widget_set_visible (sidebar->popup_menu_start_item, show_start);
+       gtk_widget_set_visible (sidebar->popup_menu_stop_item, show_stop);
+       gtk_widget_set_visible (sidebar->popup_menu_empty_trash_item, show_empty_trash);
+
+       /* Adjust start/stop items to reflect the type of the drive */
+       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Start"));
+       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Stop"));
+       if ((show_start || show_stop) && drive != NULL) {
+               switch (g_drive_get_start_stop_type (drive)) {
+               case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
+                       /* start() for type G_DRIVE_START_STOP_TYPE_SHUTDOWN is normally not used */
+                       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Power On"));
+                       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Safely Remove Drive"));
+                       break;
+               case G_DRIVE_START_STOP_TYPE_NETWORK:
+                       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Connect Drive"));
+                       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Disconnect Drive"));
+                       break;
+               case G_DRIVE_START_STOP_TYPE_MULTIDISK:
+                       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Start Multi-disk Device"));
+                       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Stop Multi-disk Device"));
+                       break;
+               case G_DRIVE_START_STOP_TYPE_PASSWORD:
+                       /* stop() for type G_DRIVE_START_STOP_TYPE_PASSWORD is normally not used */
+                       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Unlock Drive"));
+                       gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Lock Drive"));
+                       break;
+
+               default:
+               case G_DRIVE_START_STOP_TYPE_UNKNOWN:
+                       /* uses defaults set above */
+                       break;
+               }
+       }
+
+
+       g_free (uri);
+}
+
+/* Callback used when the selection in the shortcuts tree changes */
+static void
+bookmarks_selection_changed_cb (GtkTreeSelection      *selection,
+                               NautilusPlacesSidebar *sidebar)
+{
+       bookmarks_check_popup_sensitivity (sidebar);
+}
+
+static void
+volume_mounted_cb (GVolume *volume,
+                  GObject *user_data)
+{
+       GMount *mount;
+       NautilusPlacesSidebar *sidebar;
+       GFile *location;
+
+       sidebar = NAUTILUS_PLACES_SIDEBAR (user_data);
+
+       sidebar->mounting = FALSE;
+
+       mount = g_volume_get_mount (volume);
+       if (mount != NULL) {
+               location = g_mount_get_default_location (mount);
+
+               if (sidebar->go_to_after_mount_slot != NULL) {
+                       if ((sidebar->go_to_after_mount_flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW) == 0) {
+                               nautilus_window_slot_open_location (sidebar->go_to_after_mount_slot, location,
+                                                                   sidebar->go_to_after_mount_flags, NULL);
+                       } else {
+                               NautilusWindow *new, *cur;
+
+                               cur = NAUTILUS_WINDOW (sidebar->window);
+                               new = nautilus_application_create_window (nautilus_application_get_singleton (),
+                                                                         gtk_window_get_screen (GTK_WINDOW (cur)));
+                               nautilus_window_go_to (new, location);
+                       }
+               }
+
+               g_object_unref (G_OBJECT (location));
+               g_object_unref (G_OBJECT (mount));
+       }
+
+       
+       eel_remove_weak_pointer (&(sidebar->go_to_after_mount_slot));
+}
+
+static void
+drive_start_from_bookmark_cb (GObject      *source_object,
+                             GAsyncResult *res,
+                             gpointer      user_data)
+{
+       GError *error;
+       char *primary;
+       char *name;
+
+       error = NULL;
+       if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) {
+               if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+                       name = g_drive_get_name (G_DRIVE (source_object));
+                       primary = g_strdup_printf (_("Unable to start %s"), name);
+                       g_free (name);
+                       eel_show_error_dialog (primary,
+                                              error->message,
+                                              NULL);
+                       g_free (primary);
+               }
+               g_error_free (error);
+       }
+}
+
+static void
+open_selected_bookmark (NautilusPlacesSidebar *sidebar,
+                       GtkTreeModel          *model,
+                       GtkTreeIter           *iter,
+                       NautilusWindowOpenFlags       flags)
+{
+       NautilusWindowSlot *slot;
+       GFile *location;
+       char *uri;
+
+       if (!iter) {
+               return;
+       }
+
+       gtk_tree_model_get (model, iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1);
+
+       if (uri != NULL) {
+               DEBUG ("Activating bookmark %s", uri);
+
+               location = g_file_new_for_uri (uri);
+               /* Navigate to the clicked location */
+               if ((flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW) == 0) {
+                       slot = nautilus_window_get_active_slot (sidebar->window);
+                       nautilus_window_slot_open_location (slot, location,
+                                                           flags, NULL);
+               } else {
+                       NautilusWindow *cur, *new;
+                       
+                       cur = NAUTILUS_WINDOW (sidebar->window);
+                       new = nautilus_application_create_window (nautilus_application_get_singleton (),
+                                                                 gtk_window_get_screen (GTK_WINDOW (cur)));
+                       nautilus_window_go_to (new, location);
+               }
+               g_object_unref (location);
+               g_free (uri);
+
+       } else {
+               GDrive *drive;
+               GVolume *volume;
+               NautilusWindowSlot *slot;
+
+               gtk_tree_model_get (model, iter,
+                                   PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
+                                   PLACES_SIDEBAR_COLUMN_VOLUME, &volume,
+                                   -1);
+
+               if (volume != NULL && !sidebar->mounting) {
+                       sidebar->mounting = TRUE;
+
+                       g_assert (sidebar->go_to_after_mount_slot == NULL);
+
+                       slot = nautilus_window_get_active_slot (sidebar->window);
+                       sidebar->go_to_after_mount_slot = slot;
+                       eel_add_weak_pointer (&(sidebar->go_to_after_mount_slot));
+
+                       sidebar->go_to_after_mount_flags = flags;
+
+                       nautilus_file_operations_mount_volume_full (NULL, volume,
+                                                                   volume_mounted_cb,
+                                                                   G_OBJECT (sidebar));
+               } else if (volume == NULL && drive != NULL &&
+                          (g_drive_can_start (drive) || g_drive_can_start_degraded (drive))) {
+                       GMountOperation *mount_op;
+
+                       mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
+                       g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_from_bookmark_cb, NULL);
+                       g_object_unref (mount_op);
+               }
+
+               if (drive != NULL)
+                       g_object_unref (drive);
+               if (volume != NULL)
+                       g_object_unref (volume);
+       }
+}
+
+static void
+open_shortcut_from_menu (NautilusPlacesSidebar *sidebar,
+                        NautilusWindowOpenFlags               flags)
+{
+       GtkTreeModel *model;
+       GtkTreePath *path;
+       GtkTreeIter iter;
+
+       model = gtk_tree_view_get_model (sidebar->tree_view);
+       gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL);
+
+       gtk_tree_model_get_iter (model, &iter, path);
+
+       open_selected_bookmark (sidebar, model, &iter, flags);
+
+       gtk_tree_path_free (path);
+}
+
+static void
+open_shortcut_cb (GtkMenuItem          *item,
+                 NautilusPlacesSidebar *sidebar)
+{
+       open_shortcut_from_menu (sidebar, 0);
+}
+
+static void
+open_shortcut_in_new_window_cb (GtkMenuItem          *item,
+                               NautilusPlacesSidebar *sidebar)
+{
+       open_shortcut_from_menu (sidebar, NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW);
+}
+
+static void
+open_shortcut_in_new_tab_cb (GtkMenuItem             *item,
+                               NautilusPlacesSidebar *sidebar)
+{
+       open_shortcut_from_menu (sidebar, NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB);
+}
+
+/* Add bookmark for the selected item */
+static void
+add_bookmark (NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       char *uri;
+       GFile *location;
+       NautilusBookmark *bookmark;
+
+       model = gtk_tree_view_get_model (sidebar->tree_view);
+
+       if (get_selected_iter (sidebar, &iter)) {
+               gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1);
+
+               if (uri == NULL) {
+                       return;
+               }
+
+               location = g_file_new_for_uri (uri);
+               bookmark = nautilus_bookmark_new (location, NULL, NULL);
+
+               if (!nautilus_bookmark_list_contains (sidebar->bookmarks, bookmark)) {
+                       nautilus_bookmark_list_append (sidebar->bookmarks, bookmark);
+               }
+
+               g_object_unref (location);
+               g_object_unref (bookmark);
+               g_free (uri);
+       }
+}
+
+static void
+add_shortcut_cb (GtkMenuItem           *item,
+                NautilusPlacesSidebar *sidebar)
+{
+       add_bookmark (sidebar);
+}
+
+/* Rename the selected bookmark */
+static void
+rename_selected_bookmark (NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       GtkTreePath *path;
+       GtkTreeViewColumn *column;
+       GtkCellRenderer *cell;
+       GList *renderers;
+       PlaceType type;
+       
+       if (get_selected_iter (sidebar, &iter)) {
+               gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                                   PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
+                                   -1);
+
+               if (type != PLACES_BOOKMARK) {
+                       return;
+               }
+
+               path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->filter_model), &iter);
+               column = gtk_tree_view_get_column (GTK_TREE_VIEW (sidebar->tree_view), 0);
+               renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+               cell = g_list_nth_data (renderers, 6);
+               g_list_free (renderers);
+               g_object_set (cell, "editable", TRUE, NULL);
+               gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (sidebar->tree_view),
+                                               path, column, cell, TRUE);
+               gtk_tree_path_free (path);
+       }
+}
+
+static void
+rename_shortcut_cb (GtkMenuItem           *item,
+                   NautilusPlacesSidebar *sidebar)
+{
+       rename_selected_bookmark (sidebar);
+}
+
+/* Removes the selected bookmarks */
+static void
+remove_selected_bookmarks (NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       PlaceType type; 
+       int index;
+
+       if (!get_selected_iter (sidebar, &iter)) {
+               return;
+       }
+       
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
+                           -1);
+
+       if (type != PLACES_BOOKMARK) {
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_INDEX, &index,
+                           -1);
+
+       nautilus_bookmark_list_delete_item_at (sidebar->bookmarks, index);
+}
+
+static void
+remove_shortcut_cb (GtkMenuItem           *item,
+                   NautilusPlacesSidebar *sidebar)
+{
+       remove_selected_bookmarks (sidebar);
+}
+
+static void
+mount_shortcut_cb (GtkMenuItem           *item,
+                  NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       GVolume *volume;
+
+       if (!get_selected_iter (sidebar, &iter)) {
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_VOLUME, &volume,
+                           -1);
+
+       if (volume != NULL) {
+               nautilus_file_operations_mount_volume (NULL, volume);
+               g_object_unref (volume);
+       }
+}
+
+static void
+unmount_done (gpointer data)
+{
+       NautilusWindow *window;
+
+       window = data;
+       nautilus_window_set_initiated_unmount (window, FALSE);
+       g_object_unref (window);
+}
+
+static void
+do_unmount (GMount *mount,
+           NautilusPlacesSidebar *sidebar)
+{
+       if (mount != NULL) {
+               nautilus_window_set_initiated_unmount (sidebar->window, TRUE);
+               nautilus_file_operations_unmount_mount_full (NULL, mount, FALSE, TRUE,
+                                                            unmount_done,
+                                                            g_object_ref (sidebar->window));
+       }
+}
+
+static void
+do_unmount_selection (NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       GMount *mount;
+
+       if (!get_selected_iter (sidebar, &iter)) {
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_MOUNT, &mount,
+                           -1);
+
+       if (mount != NULL) {
+               do_unmount (mount, sidebar);
+               g_object_unref (mount);
+       }
+}
+
+static void
+unmount_shortcut_cb (GtkMenuItem           *item,
+                    NautilusPlacesSidebar *sidebar)
+{
+       do_unmount_selection (sidebar);
+}
+
+static void
+drive_eject_cb (GObject *source_object,
+               GAsyncResult *res,
+               gpointer user_data)
+{
+       NautilusWindow *window;
+       GError *error;
+       char *primary;
+       char *name;
+
+       window = user_data;
+       nautilus_window_set_initiated_unmount (window, FALSE);
+       g_object_unref (window);
+
+       error = NULL;
+       if (!g_drive_eject_with_operation_finish (G_DRIVE (source_object), res, &error)) {
+               if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+                       name = g_drive_get_name (G_DRIVE (source_object));
+                       primary = g_strdup_printf (_("Unable to eject %s"), name);
+                       g_free (name);
+                       eel_show_error_dialog (primary,
+                                              error->message,
+                                      NULL);
+                       g_free (primary);
+               }
+               g_error_free (error);
+       }
+}
+
+static void
+volume_eject_cb (GObject *source_object,
+               GAsyncResult *res,
+               gpointer user_data)
+{
+       NautilusWindow *window;
+       GError *error;
+       char *primary;
+       char *name;
+
+       window = user_data;
+       nautilus_window_set_initiated_unmount (window, FALSE);
+       g_object_unref (window);
+
+       error = NULL;
+       if (!g_volume_eject_with_operation_finish (G_VOLUME (source_object), res, &error)) {
+               if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+                       name = g_volume_get_name (G_VOLUME (source_object));
+                       primary = g_strdup_printf (_("Unable to eject %s"), name);
+                       g_free (name);
+                       eel_show_error_dialog (primary,
+                                              error->message,
+                                              NULL);
+                       g_free (primary);
+               }
+               g_error_free (error);
+       }
+}
+
+static void
+mount_eject_cb (GObject *source_object,
+               GAsyncResult *res,
+               gpointer user_data)
+{
+       NautilusWindow *window;
+       GError *error;
+       char *primary;
+       char *name;
+
+       window = user_data;
+       nautilus_window_set_initiated_unmount (window, FALSE);
+       g_object_unref (window);
+
+       error = NULL;
+       if (!g_mount_eject_with_operation_finish (G_MOUNT (source_object), res, &error)) {
+               if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+                       name = g_mount_get_name (G_MOUNT (source_object));
+                       primary = g_strdup_printf (_("Unable to eject %s"), name);
+                       g_free (name);
+                       eel_show_error_dialog (primary,
+                                              error->message,
+                                              NULL);
+                       g_free (primary);
+               }
+               g_error_free (error);
+       }
+}
+
+static void
+do_eject (GMount *mount,
+         GVolume *volume,
+         GDrive *drive,
+         NautilusPlacesSidebar *sidebar)
+{
+       GMountOperation *mount_op;
+
+       mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
+       if (mount != NULL) {
+               nautilus_window_set_initiated_unmount (sidebar->window, TRUE);
+               g_mount_eject_with_operation (mount, 0, mount_op, NULL, mount_eject_cb,
+                                             g_object_ref (sidebar->window));
+       } else if (volume != NULL) {
+               nautilus_window_set_initiated_unmount (sidebar->window, TRUE);
+               g_volume_eject_with_operation (volume, 0, mount_op, NULL, volume_eject_cb,
+                                             g_object_ref (sidebar->window));
+       } else if (drive != NULL) {
+               nautilus_window_set_initiated_unmount (sidebar->window, TRUE);
+               g_drive_eject_with_operation (drive, 0, mount_op, NULL, drive_eject_cb,
+                                             g_object_ref (sidebar->window));
+       }
+       g_object_unref (mount_op);
+}
+
+static void
+eject_shortcut_cb (GtkMenuItem           *item,
+                  NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       GMount *mount;
+       GVolume *volume;
+       GDrive *drive;
+
+       if (!get_selected_iter (sidebar, &iter)) {
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_MOUNT, &mount,
+                           PLACES_SIDEBAR_COLUMN_VOLUME, &volume,
+                           PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
+                           -1);
+
+       do_eject (mount, volume, drive, sidebar);
+}
+
+static gboolean
+eject_or_unmount_bookmark (NautilusPlacesSidebar *sidebar,
+                          GtkTreePath *path)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gboolean can_unmount, can_eject;
+       GMount *mount;
+       GVolume *volume;
+       GDrive *drive;
+       gboolean ret;
+
+       model = GTK_TREE_MODEL (sidebar->filter_model);
+
+       if (!path) {
+               return FALSE;
+       }
+       if (!gtk_tree_model_get_iter (model, &iter, path)) {
+               return FALSE;
+       }
+
+       gtk_tree_model_get (model, &iter,
+                           PLACES_SIDEBAR_COLUMN_MOUNT, &mount,
+                           PLACES_SIDEBAR_COLUMN_VOLUME, &volume,
+                           PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
+                           -1);
+
+       ret = FALSE;
+
+       check_unmount_and_eject (mount, volume, drive, &can_unmount, &can_eject);
+       /* if we can eject, it has priority over unmount */
+       if (can_eject) {
+               do_eject (mount, volume, drive, sidebar);
+               ret = TRUE;
+       } else if (can_unmount) {
+               do_unmount (mount, sidebar);
+               ret = TRUE;
+       }
+
+       if (mount != NULL)
+               g_object_unref (mount);
+       if (volume != NULL)
+               g_object_unref (volume);
+       if (drive != NULL)
+               g_object_unref (drive);
+
+       return ret;
+}
+
+static gboolean
+eject_or_unmount_selection (NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       GtkTreePath *path;
+       gboolean ret;
+
+       if (!get_selected_iter (sidebar, &iter)) {
+               return FALSE;
+       }
+
+       path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->filter_model), &iter);
+       if (path == NULL) {
+               return FALSE;
+       }
+
+       ret = eject_or_unmount_bookmark (sidebar, path);
+
+       gtk_tree_path_free (path);
+
+       return ret;
+}
+
+static void
+drive_poll_for_media_cb (GObject *source_object,
+                        GAsyncResult *res,
+                        gpointer user_data)
+{
+       GError *error;
+       char *primary;
+       char *name;
+
+       error = NULL;
+       if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) {
+               if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+                       name = g_drive_get_name (G_DRIVE (source_object));
+                       primary = g_strdup_printf (_("Unable to poll %s for media changes"), name);
+                       g_free (name);
+                       eel_show_error_dialog (primary,
+                                              error->message,
+                                              NULL);
+                       g_free (primary);
+               }
+               g_error_free (error);
+       }
+}
+
+static void
+rescan_shortcut_cb (GtkMenuItem           *item,
+                   NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       GDrive  *drive;
+
+       if (!get_selected_iter (sidebar, &iter)) {
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
+                           -1);
+
+       if (drive != NULL) {
+               g_drive_poll_for_media (drive, NULL, drive_poll_for_media_cb, NULL);
+       }
+       g_object_unref (drive);
+}
+
+static void
+drive_start_cb (GObject      *source_object,
+               GAsyncResult *res,
+               gpointer      user_data)
+{
+       GError *error;
+       char *primary;
+       char *name;
+
+       error = NULL;
+       if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) {
+               if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+                       name = g_drive_get_name (G_DRIVE (source_object));
+                       primary = g_strdup_printf (_("Unable to start %s"), name);
+                       g_free (name);
+                       eel_show_error_dialog (primary,
+                                              error->message,
+                                              NULL);
+                       g_free (primary);
+               }
+               g_error_free (error);
+       }
+}
+
+static void
+start_shortcut_cb (GtkMenuItem           *item,
+                  NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       GDrive  *drive;
+
+       if (!get_selected_iter (sidebar, &iter)) {
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
+                           -1);
+
+       if (drive != NULL) {
+               GMountOperation *mount_op;
+
+               mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
+
+               g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_cb, NULL);
+
+               g_object_unref (mount_op);
+       }
+       g_object_unref (drive);
+}
+
+static void
+drive_stop_cb (GObject *source_object,
+              GAsyncResult *res,
+              gpointer user_data)
+{
+       NautilusWindow *window;
+       GError *error;
+       char *primary;
+       char *name;
+
+       window = user_data;
+       nautilus_window_set_initiated_unmount (window, FALSE);
+       g_object_unref (window);
+
+       error = NULL;
+       if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) {
+               if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+                       name = g_drive_get_name (G_DRIVE (source_object));
+                       primary = g_strdup_printf (_("Unable to stop %s"), name);
+                       g_free (name);
+                       eel_show_error_dialog (primary,
+                                              error->message,
+                                              NULL);
+                       g_free (primary);
+               }
+               g_error_free (error);
+       }
+}
+
+static void
+stop_shortcut_cb (GtkMenuItem           *item,
+                 NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeIter iter;
+       GDrive  *drive;
+
+       if (!get_selected_iter (sidebar, &iter)) {
+               return;
+       }
+
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
+                           -1);
+
+       if (drive != NULL) {
+               GMountOperation *mount_op;
+
+               mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
+               nautilus_window_set_initiated_unmount (sidebar->window, TRUE);
+               g_drive_stop (drive, G_MOUNT_UNMOUNT_NONE, mount_op, NULL, drive_stop_cb,
+                             g_object_ref (sidebar->window));
+               g_object_unref (mount_op);
+       }
+       g_object_unref (drive);
+}
+
+static void
+empty_trash_cb (GtkMenuItem           *item,
+               NautilusPlacesSidebar *sidebar)
+{
+       nautilus_file_operations_empty_trash (GTK_WIDGET (sidebar->window));
+}
+
+static gboolean
+find_prev_or_next_row (NautilusPlacesSidebar *sidebar,
+                      GtkTreeIter *iter,
+                      gboolean go_up)
+{
+       GtkTreeSelection *selection;
+       GtkTreeModel *model;
+       int place_type;
+       gboolean res;
+
+       selection = gtk_tree_view_get_selection (sidebar->tree_view);
+       res = gtk_tree_selection_get_selected (selection, &model, iter);
+
+       if (!res) {
+               goto out;
+       }
+
+       if (go_up) {
+               res = gtk_tree_model_iter_previous (model, iter);
+       } else {
+               res = gtk_tree_model_iter_next (model, iter);
+       }
+
+       if (res) {
+               gtk_tree_model_get (model, iter,
+                                   PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
+                                   -1);
+               if (place_type == PLACES_HEADING) {
+                       if (go_up) {
+                               res = gtk_tree_model_iter_previous (model, iter);
+                       } else {
+                               res = gtk_tree_model_iter_next (model, iter);
+                       }
+               }
+       }
+
+ out:
+       return res;
+}
+
+static gboolean
+find_prev_row (NautilusPlacesSidebar *sidebar, GtkTreeIter *iter)
+{
+       return find_prev_or_next_row (sidebar, iter, TRUE);
+}
+
+static gboolean
+find_next_row (NautilusPlacesSidebar *sidebar, GtkTreeIter *iter)
+{
+       return find_prev_or_next_row (sidebar, iter, FALSE);
+}
+
+/* Handler for GtkWidget::key-press-event on the shortcuts list */
+static gboolean
+bookmarks_key_press_event_cb (GtkWidget             *widget,
+                             GdkEventKey           *event,
+                             NautilusPlacesSidebar *sidebar)
+{
+  guint modifiers;
+  GtkTreeIter iter;
+
+  modifiers = gtk_accelerator_get_default_mod_mask ();
+
+  if ((event->keyval == GDK_KEY_Return ||
+       event->keyval == GDK_KEY_KP_Enter ||
+       event->keyval == GDK_KEY_ISO_Enter ||
+       event->keyval == GDK_KEY_space)) {
+
+      GtkTreeModel *model;
+      GtkTreeSelection *selection;
+      GtkTreeIter iter;
+      NautilusWindowOpenFlags flags = 0;
+
+      if ((event->state & modifiers) == GDK_SHIFT_MASK) {
+          flags = NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB;
+      } else if ((event->state & modifiers) == GDK_CONTROL_MASK) {
+          flags = NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW;
+      }
+
+      model = gtk_tree_view_get_model (sidebar->tree_view);
+      selection = gtk_tree_view_get_selection (sidebar->tree_view);
+      gtk_tree_selection_get_selected (selection, NULL, &iter);
+
+      open_selected_bookmark (sidebar, model, &iter, flags);
+
+      return TRUE;
+  }
+
+  if (event->keyval == GDK_KEY_Down &&
+      (event->state & modifiers) == GDK_MOD1_MASK) {
+      return eject_or_unmount_selection (sidebar);
+  }
+
+  if (event->keyval == GDK_KEY_Up) {
+      if (find_prev_row (sidebar, &iter)) {
+             gtk_tree_selection_select_iter (gtk_tree_view_get_selection (sidebar->tree_view),
+                                             &iter);
+      }
+      return TRUE;
+  }
+
+  if (event->keyval == GDK_KEY_Down) {
+      if (find_next_row (sidebar, &iter)) {
+             gtk_tree_selection_select_iter (gtk_tree_view_get_selection (sidebar->tree_view),
+                                             &iter);
+      }
+      return TRUE;
+  }
+
+  if ((event->keyval == GDK_KEY_Delete
+      || event->keyval == GDK_KEY_KP_Delete)
+      && (event->state & modifiers) == 0) {
+      remove_selected_bookmarks (sidebar);
+      return TRUE;
+  }
+
+  if ((event->keyval == GDK_KEY_F2)
+      && (event->state & modifiers) == 0) {
+      rename_selected_bookmark (sidebar);
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* Constructs the popup menu for the file list if needed */
+static void
+bookmarks_build_popup_menu (NautilusPlacesSidebar *sidebar)
+{
+       GtkWidget *item;
+       gboolean use_browser;
+       
+       if (sidebar->popup_menu) {
+               return;
+       }
+
+       use_browser = g_settings_get_boolean (nautilus_preferences,
+                                             NAUTILUS_PREFERENCES_ALWAYS_USE_BROWSER);
+
+       sidebar->popup_menu = gtk_menu_new ();
+       gtk_menu_attach_to_widget (GTK_MENU (sidebar->popup_menu),
+                                  GTK_WIDGET (sidebar),
+                                  bookmarks_popup_menu_detach_cb);
+       
+       item = gtk_image_menu_item_new_with_mnemonic (_("_Open"));
+       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+                                      gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
+       g_signal_connect (item, "activate",
+                         G_CALLBACK (open_shortcut_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       item = gtk_menu_item_new_with_mnemonic (_("Open in New _Tab"));
+       sidebar->popup_menu_open_in_new_tab_item = item;
+       g_signal_connect (item, "activate",
+                         G_CALLBACK (open_shortcut_in_new_tab_cb), sidebar);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       if (use_browser) {
+               gtk_widget_show (item);
+       }
+
+       item = gtk_menu_item_new_with_mnemonic (_("Open in New _Window"));
+       g_signal_connect (item, "activate",
+                         G_CALLBACK (open_shortcut_in_new_window_cb), sidebar);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       if (use_browser) {
+               gtk_widget_show (item);
+       }
+
+       eel_gtk_menu_append_separator (GTK_MENU (sidebar->popup_menu));
+
+       item = gtk_menu_item_new_with_mnemonic (_("_Add Bookmark"));
+       sidebar->popup_menu_add_shortcut_item = item;
+       g_signal_connect (item, "activate",
+                         G_CALLBACK (add_shortcut_cb), sidebar);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       item = gtk_image_menu_item_new_with_label (_("Remove"));
+       sidebar->popup_menu_remove_item = item;
+       gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+                                gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
+       g_signal_connect (item, "activate",
+                   G_CALLBACK (remove_shortcut_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+       
+       item = gtk_menu_item_new_with_label (_("Rename..."));
+       sidebar->popup_menu_rename_item = item;
+       g_signal_connect (item, "activate",
+                   G_CALLBACK (rename_shortcut_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+       
+       /* Mount/Unmount/Eject menu items */
+
+       sidebar->popup_menu_separator_item =
+               GTK_WIDGET (eel_gtk_menu_append_separator (GTK_MENU (sidebar->popup_menu)));
+
+       item = gtk_menu_item_new_with_mnemonic (_("_Mount"));
+       sidebar->popup_menu_mount_item = item;
+       g_signal_connect (item, "activate",
+                   G_CALLBACK (mount_shortcut_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       item = gtk_menu_item_new_with_mnemonic (_("_Unmount"));
+       sidebar->popup_menu_unmount_item = item;
+       g_signal_connect (item, "activate",
+                   G_CALLBACK (unmount_shortcut_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       item = gtk_menu_item_new_with_mnemonic (_("_Eject"));
+       sidebar->popup_menu_eject_item = item;
+       g_signal_connect (item, "activate",
+                   G_CALLBACK (eject_shortcut_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       item = gtk_menu_item_new_with_mnemonic (_("_Detect Media"));
+       sidebar->popup_menu_rescan_item = item;
+       g_signal_connect (item, "activate",
+                   G_CALLBACK (rescan_shortcut_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       item = gtk_menu_item_new_with_mnemonic (_("_Start"));
+       sidebar->popup_menu_start_item = item;
+       g_signal_connect (item, "activate",
+                         G_CALLBACK (start_shortcut_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       item = gtk_menu_item_new_with_mnemonic (_("_Stop"));
+       sidebar->popup_menu_stop_item = item;
+       g_signal_connect (item, "activate",
+                         G_CALLBACK (stop_shortcut_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       /* Empty Trash menu item */
+
+       item = gtk_menu_item_new_with_mnemonic (_("Empty _Trash"));
+       sidebar->popup_menu_empty_trash_item = item;
+       g_signal_connect (item, "activate",
+                   G_CALLBACK (empty_trash_cb), sidebar);
+       gtk_widget_show (item);
+       gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
+
+       bookmarks_check_popup_sensitivity (sidebar);
+}
+
+static void
+bookmarks_update_popup_menu (NautilusPlacesSidebar *sidebar)
+{
+       bookmarks_build_popup_menu (sidebar);  
+}
+
+static void
+bookmarks_popup_menu (NautilusPlacesSidebar *sidebar,
+                     GdkEventButton        *event)
+{
+       bookmarks_update_popup_menu (sidebar);
+       eel_pop_up_context_menu (GTK_MENU(sidebar->popup_menu),
+                             EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+                             EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+                             event);
+}
+
+/* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
+static gboolean
+bookmarks_popup_menu_cb (GtkWidget *widget,
+                        NautilusPlacesSidebar *sidebar)
+{
+       bookmarks_popup_menu (sidebar, NULL);
+       return TRUE;
+}
+
+static gboolean
+bookmarks_button_release_event_cb (GtkWidget *widget,
+                                  GdkEventButton *event,
+                                  NautilusPlacesSidebar *sidebar)
+{
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       GtkTreeModel *model;
+       GtkTreeView *tree_view;
+       gboolean res;
+
+       path = NULL;
+
+       if (event->type != GDK_BUTTON_RELEASE) {
+               return TRUE;
+       }
+
+       if (clicked_eject_button (sidebar, &path)) {
+               eject_or_unmount_bookmark (sidebar, path);
+               gtk_tree_path_free (path);
+
+               return FALSE;
+       }
+
+       tree_view = GTK_TREE_VIEW (widget);
+       model = gtk_tree_view_get_model (tree_view);
+
+       if (event->button == 1) {
+
+               if (event->window != gtk_tree_view_get_bin_window (tree_view)) {
+                       return FALSE;
+               }
+
+               res = gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y,
+                                                    &path, NULL, NULL, NULL);
+
+               if (!res) {
+                       return FALSE;
+               }
+
+               gtk_tree_model_get_iter (model, &iter, path);
+
+               open_selected_bookmark (sidebar, model, &iter, 0);
+
+               gtk_tree_path_free (path);
+       }
+
+       return FALSE;
+}
+
+static void
+update_eject_buttons (NautilusPlacesSidebar *sidebar,
+                     GtkTreePath           *path)
+{
+       GtkTreeIter iter;
+       gboolean icon_visible, path_same;
+
+       icon_visible = TRUE;
+
+       if (path == NULL && sidebar->eject_highlight_path == NULL) {
+               /* Both are null - highlight up to date */
+               return;
+       }
+
+       path_same = (path != NULL) &&
+               (sidebar->eject_highlight_path != NULL) &&
+               (gtk_tree_path_compare (sidebar->eject_highlight_path, path) == 0);
+
+       if (path_same) {
+               /* Same path - highlight up to date */
+               return;
+       }
+
+       if (path) {
+               gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->filter_model),
+                                        &iter,
+                                        path);
+
+               gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model),
+                                   &iter,
+                                   PLACES_SIDEBAR_COLUMN_EJECT, &icon_visible,
+                                   -1);
+       }
+
+       if (!icon_visible || path == NULL || !path_same) {
+               /* remove highlighting and reset the saved path, as we are leaving
+                * an eject button area.
+                */
+               if (sidebar->eject_highlight_path) {
+                       gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store),
+                                                &iter,
+                                                sidebar->eject_highlight_path);
+
+                       gtk_list_store_set (sidebar->store,
+                                           &iter,
+                                           PLACES_SIDEBAR_COLUMN_EJECT_ICON, get_eject_icon (sidebar, FALSE),
+                                           -1);
+                       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->filter_model));
+
+                       gtk_tree_path_free (sidebar->eject_highlight_path);
+                       sidebar->eject_highlight_path = NULL;
+               }
+
+               if (!icon_visible) {
+                       return;
+               }
+       }
+
+       if (path != NULL) {
+               /* add highlighting to the selected path, as the icon is visible and
+                * we're hovering it.
+                */
+               gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store),
+                                        &iter,
+                                        path);
+               gtk_list_store_set (sidebar->store,
+                                   &iter,
+                                   PLACES_SIDEBAR_COLUMN_EJECT_ICON, get_eject_icon (sidebar, TRUE),
+                                   -1);
+               gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->filter_model));
+
+               sidebar->eject_highlight_path = gtk_tree_path_copy (path);
+       }
+}
+
+static gboolean
+bookmarks_motion_event_cb (GtkWidget             *widget,
+                          GdkEventMotion        *event,
+                          NautilusPlacesSidebar *sidebar)
+{
+       GtkTreePath *path;
+
+       path = NULL;
+
+       if (over_eject_button (sidebar, event->x, event->y, &path)) {
+               update_eject_buttons (sidebar, path);
+               gtk_tree_path_free (path);
+
+               return TRUE;
+       }
+
+       update_eject_buttons (sidebar, NULL);
+
+       return FALSE;
+}
+
+/* Callback used when a button is pressed on the shortcuts list.  
+ * We trap button 3 to bring up a popup menu, and button 2 to
+ * open in a new tab.
+ */
+static gboolean
+bookmarks_button_press_event_cb (GtkWidget             *widget,
+                                GdkEventButton        *event,
+                                NautilusPlacesSidebar *sidebar)
+{
+       if (event->type != GDK_BUTTON_PRESS) {
+               /* ignore multiple clicks */
+               return TRUE;
+       }
+
+       if (event->button == 3) {
+               bookmarks_popup_menu (sidebar, event);
+       } else if (event->button == 2) {
+               GtkTreeModel *model;
+               GtkTreePath *path;
+               GtkTreeIter iter;
+               GtkTreeView *tree_view;
+               NautilusWindowOpenFlags flags = 0;
+
+               tree_view = GTK_TREE_VIEW (widget);
+               g_assert (tree_view == sidebar->tree_view);
+
+               model = gtk_tree_view_get_model (tree_view);
+
+               gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y, 
+                                              &path, NULL, NULL, NULL);
+               gtk_tree_model_get_iter (model, &iter, path);
+
+               if (g_settings_get_boolean (nautilus_preferences,
+                                           NAUTILUS_PREFERENCES_ALWAYS_USE_BROWSER)) {
+                       flags = (event->state & GDK_CONTROL_MASK) ?
+                               NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW :
+                               NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB;
+               } else {
+                       flags = NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND;
+               }
+
+               open_selected_bookmark (sidebar, model, &iter, flags);
+
+               if (path != NULL) {
+                       gtk_tree_path_free (path);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+
+static void
+bookmarks_edited (GtkCellRenderer       *cell,
+                 gchar                 *path_string,
+                 gchar                 *new_text,
+                 NautilusPlacesSidebar *sidebar)
+{
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       NautilusBookmark *bookmark;
+       int index;
+
+       g_object_set (cell, "editable", FALSE, NULL);
+       
+       path = gtk_tree_path_new_from_string (path_string);
+       gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->filter_model), &iter, path);
+       gtk_tree_model_get (GTK_TREE_MODEL (sidebar->filter_model), &iter,
+                           PLACES_SIDEBAR_COLUMN_INDEX, &index,
+                           -1);
+       gtk_tree_path_free (path);
+       bookmark = nautilus_bookmark_list_item_at (sidebar->bookmarks, index);
+
+       if (bookmark != NULL) {
+               nautilus_bookmark_set_custom_name (bookmark, new_text);
+       }
+}
+
+static void
+bookmarks_editing_canceled (GtkCellRenderer       *cell,
+                           NautilusPlacesSidebar *sidebar)
+{
+       g_object_set (cell, "editable", FALSE, NULL);
+}
+
+static void
+trash_state_changed_cb (NautilusTrashMonitor *trash_monitor,
+                       gboolean             state,
+                       gpointer             data)
+{
+       NautilusPlacesSidebar *sidebar;
+
+       sidebar = NAUTILUS_PLACES_SIDEBAR (data);
+
+       /* The trash icon changed, update the sidebar */
+       update_places (sidebar);
+
+       bookmarks_check_popup_sensitivity (sidebar);
+}
+
+static gboolean
+tree_selection_func (GtkTreeSelection *selection,
+                    GtkTreeModel *model,
+                    GtkTreePath *path,
+                    gboolean path_currently_selected,
+                    gpointer user_data)
+{
+       GtkTreeIter iter;
+       PlaceType row_type;
+
+       gtk_tree_model_get_iter (model, &iter, path);
+       gtk_tree_model_get (model, &iter,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type,
+                           -1);
+
+       if (row_type == PLACES_HEADING) {
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void
+icon_cell_renderer_func (GtkTreeViewColumn *column,
+                        GtkCellRenderer *cell,
+                        GtkTreeModel *model,
+                        GtkTreeIter *iter,
+                        gpointer user_data)
+{
+       PlaceType type;
+
+       gtk_tree_model_get (model, iter,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
+                           -1);
+
+       if (type == PLACES_HEADING) {
+               g_object_set (cell,
+                             "visible", FALSE,
+                             NULL);
+       } else {
+               g_object_set (cell,
+                             "visible", TRUE,
+                             NULL);
+       }
+}
+
+static void
+padding_cell_renderer_func (GtkTreeViewColumn *column,
+                           GtkCellRenderer *cell,
+                           GtkTreeModel *model,
+                           GtkTreeIter *iter,
+                           gpointer user_data)
+{
+       PlaceType type;
+
+       gtk_tree_model_get (model, iter,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
+                           -1);
+
+       if (type == PLACES_HEADING) {
+               g_object_set (cell,
+                             "visible", FALSE,
+                             "xpad", 0,
+                             "ypad", 0,
+                             NULL);
+       } else {
+               g_object_set (cell,
+                             "visible", TRUE,
+                             "xpad", 3,
+                             "ypad", 3,
+                             NULL);
+       }
+}
+
+static void
+heading_cell_renderer_func (GtkTreeViewColumn *column,
+                           GtkCellRenderer *cell,
+                           GtkTreeModel *model,
+                           GtkTreeIter *iter,
+                           gpointer user_data)
+{
+       PlaceType type;
+
+       gtk_tree_model_get (model, iter,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
+                           -1);
+
+       if (type == PLACES_HEADING) {
+               g_object_set (cell,
+                             "visible", TRUE,
+                             NULL);
+       } else {
+               g_object_set (cell,
+                             "visible", FALSE,
+                             NULL);
+       }
+}
+
+static void
+nautilus_places_sidebar_init (NautilusPlacesSidebar *sidebar)
+{
+       GtkTreeView       *tree_view;
+       GtkTreeViewColumn *col;
+       GtkCellRenderer   *cell;
+       GtkTreeSelection  *selection;
+
+       sidebar->volume_monitor = g_volume_monitor_get ();
+       
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar),
+                                       GTK_POLICY_NEVER,
+                                       GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL);
+       gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sidebar), GTK_SHADOW_IN);
+
+       gtk_style_context_set_junction_sides (gtk_widget_get_style_context (GTK_WIDGET (sidebar)),
+                                             GTK_JUNCTION_RIGHT | GTK_JUNCTION_LEFT);
+
+       /* tree view */
+       tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
+       gtk_tree_view_set_headers_visible (tree_view, FALSE);
+
+       col = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ());
+
+       /* initial padding */
+       cell = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (col, cell, FALSE);
+       g_object_set (cell,
+                     "xpad", 6,
+                     NULL);
+
+       /* headings */
+       cell = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (col, cell, FALSE);
+       gtk_tree_view_column_set_attributes (col, cell,
+                                            "text", PLACES_SIDEBAR_COLUMN_HEADING_TEXT,
+                                            NULL);
+       g_object_set (cell,
+                     "weight", PANGO_WEIGHT_BOLD,
+                     "weight-set", TRUE,
+                     "ypad", 6,
+                     "xpad", 0,
+                     NULL);
+       gtk_tree_view_column_set_cell_data_func (col, cell,
+                                                heading_cell_renderer_func,
+                                                sidebar, NULL);
+
+       /* icon padding */
+       cell = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (col, cell, FALSE);
+       gtk_tree_view_column_set_cell_data_func (col, cell,
+                                                padding_cell_renderer_func,
+                                                sidebar, NULL);
+
+       /* icon renderer */
+       cell = gtk_cell_renderer_pixbuf_new ();
+       gtk_tree_view_column_pack_start (col, cell, FALSE);
+       gtk_tree_view_column_set_attributes (col, cell,
+                                            "pixbuf", PLACES_SIDEBAR_COLUMN_ICON,
+                                            NULL);
+       gtk_tree_view_column_set_cell_data_func (col, cell,
+                                                icon_cell_renderer_func,
+                                                sidebar, NULL);
+
+       /* eject text renderer */
+       cell = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (col, cell, TRUE);
+       gtk_tree_view_column_set_attributes (col, cell,
+                                            "text", PLACES_SIDEBAR_COLUMN_NAME,
+                                            "visible", PLACES_SIDEBAR_COLUMN_EJECT,
+                                            NULL);
+       g_object_set (cell,
+                     "ellipsize", PANGO_ELLIPSIZE_END,
+                     "ellipsize-set", TRUE,
+                     NULL);
+
+       /* eject icon renderer */
+       cell = gtk_cell_renderer_pixbuf_new ();
+       sidebar->eject_icon_cell_renderer = cell;
+       g_object_set (cell,
+                     "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
+                     "stock-size", GTK_ICON_SIZE_MENU,
+                     "xpad", EJECT_BUTTON_XPAD,
+                     /* align right, because for some reason gtk+ expands
+                        this even though we tell it not to. */
+                     "xalign", 1.0,
+                     NULL);
+       gtk_tree_view_column_pack_start (col, cell, FALSE);
+       gtk_tree_view_column_set_attributes (col, cell,
+                                            "visible", PLACES_SIDEBAR_COLUMN_EJECT,
+                                            "pixbuf", PLACES_SIDEBAR_COLUMN_EJECT_ICON,
+                                            NULL);
+
+       /* normal text renderer */
+       cell = gtk_cell_renderer_text_new ();
+       gtk_tree_view_column_pack_start (col, cell, TRUE);
+       g_object_set (G_OBJECT (cell), "editable", FALSE, NULL);
+       gtk_tree_view_column_set_attributes (col, cell,
+                                            "text", PLACES_SIDEBAR_COLUMN_NAME,
+                                            "visible", PLACES_SIDEBAR_COLUMN_NO_EJECT,
+                                            "editable-set", PLACES_SIDEBAR_COLUMN_BOOKMARK,
+                                            NULL);
+       g_object_set (cell,
+                     "ellipsize", PANGO_ELLIPSIZE_END,
+                     "ellipsize-set", TRUE,
+                     NULL);
+
+       g_signal_connect (cell, "edited", 
+                         G_CALLBACK (bookmarks_edited), sidebar);
+       g_signal_connect (cell, "editing-canceled", 
+                         G_CALLBACK (bookmarks_editing_canceled), sidebar);
+
+       /* this is required to align the eject buttons to the right */
+       gtk_tree_view_column_set_max_width (GTK_TREE_VIEW_COLUMN (col), NAUTILUS_ICON_SIZE_SMALLER);
+       gtk_tree_view_append_column (tree_view, col);
+
+       sidebar->store = gtk_list_store_new (PLACES_SIDEBAR_COLUMN_COUNT,
+                                            G_TYPE_INT, 
+                                            G_TYPE_STRING,
+                                            G_TYPE_DRIVE,
+                                            G_TYPE_VOLUME,
+                                            G_TYPE_MOUNT,
+                                            G_TYPE_STRING,
+                                            GDK_TYPE_PIXBUF,
+                                            G_TYPE_INT,
+                                            G_TYPE_BOOLEAN,
+                                            G_TYPE_BOOLEAN,
+                                            G_TYPE_BOOLEAN,
+                                            G_TYPE_STRING,
+                                            GDK_TYPE_PIXBUF,
+                                            G_TYPE_INT,
+                                            G_TYPE_STRING);
+
+       gtk_tree_view_set_tooltip_column (tree_view, PLACES_SIDEBAR_COLUMN_TOOLTIP);
+
+       sidebar->filter_model = nautilus_shortcuts_model_filter_new (sidebar,
+                                                                    GTK_TREE_MODEL (sidebar->store),
+                                                                    NULL);
+
+       gtk_tree_view_set_model (tree_view, sidebar->filter_model);
+       gtk_container_add (GTK_CONTAINER (sidebar), GTK_WIDGET (tree_view));
+       gtk_widget_show (GTK_WIDGET (tree_view));
+
+       gtk_widget_show (GTK_WIDGET (sidebar));
+       sidebar->tree_view = tree_view;
+
+       gtk_tree_view_set_search_column (tree_view, PLACES_SIDEBAR_COLUMN_NAME);
+       selection = gtk_tree_view_get_selection (tree_view);
+       gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+
+       gtk_tree_selection_set_select_function (selection,
+                                               tree_selection_func,
+                                               sidebar,
+                                               NULL);
+
+       gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree_view),
+                                               GDK_BUTTON1_MASK,
+                                               nautilus_shortcuts_source_targets,
+                                               G_N_ELEMENTS (nautilus_shortcuts_source_targets),
+                                               GDK_ACTION_MOVE);
+       gtk_drag_dest_set (GTK_WIDGET (tree_view),
+                          0,
+                          nautilus_shortcuts_drop_targets, G_N_ELEMENTS (nautilus_shortcuts_drop_targets),
+                          GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
+
+       g_signal_connect (tree_view, "key-press-event",
+                         G_CALLBACK (bookmarks_key_press_event_cb), sidebar);
+
+       g_signal_connect (tree_view, "drag-motion",
+                         G_CALLBACK (drag_motion_callback), sidebar);
+       g_signal_connect (tree_view, "drag-leave",
+                         G_CALLBACK (drag_leave_callback), sidebar);
+       g_signal_connect (tree_view, "drag-data-received",
+                         G_CALLBACK (drag_data_received_callback), sidebar);
+       g_signal_connect (tree_view, "drag-drop",
+                         G_CALLBACK (drag_drop_callback), sidebar);
+
+       g_signal_connect (selection, "changed",
+                         G_CALLBACK (bookmarks_selection_changed_cb), sidebar);
+       g_signal_connect (tree_view, "popup-menu",
+                         G_CALLBACK (bookmarks_popup_menu_cb), sidebar);
+       g_signal_connect (tree_view, "button-press-event",
+                         G_CALLBACK (bookmarks_button_press_event_cb), sidebar);
+       g_signal_connect (tree_view, "motion-notify-event",
+                         G_CALLBACK (bookmarks_motion_event_cb), sidebar);
+       g_signal_connect (tree_view, "button-release-event",
+                         G_CALLBACK (bookmarks_button_release_event_cb), sidebar);
+
+       eel_gtk_tree_view_set_activate_on_single_click (sidebar->tree_view,
+                                                       TRUE);
+
+       g_signal_connect_swapped (nautilus_preferences, "changed::" NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR,
+                                 G_CALLBACK(desktop_setting_changed_callback),
+                                 sidebar);
+
+       g_signal_connect_swapped (gnome_background_preferences, "changed::" NAUTILUS_PREFERENCES_SHOW_DESKTOP,
+                                 G_CALLBACK(desktop_setting_changed_callback),
+                                 sidebar);
+
+       g_signal_connect_object (nautilus_trash_monitor_get (),
+                                "trash_state_changed",
+                                G_CALLBACK (trash_state_changed_cb),
+                                sidebar, 0);
+}
+
+static void
+nautilus_places_sidebar_dispose (GObject *object)
+{
+       NautilusPlacesSidebar *sidebar;
+
+       sidebar = NAUTILUS_PLACES_SIDEBAR (object);
+
+       sidebar->window = NULL;
+       sidebar->tree_view = NULL;
+
+       g_free (sidebar->uri);
+       sidebar->uri = NULL;
+
+       free_drag_data (sidebar);
+
+       if (sidebar->eject_highlight_path != NULL) {
+               gtk_tree_path_free (sidebar->eject_highlight_path);
+               sidebar->eject_highlight_path = NULL;
+       }
+
+       if (sidebar->bookmarks_changed_id != 0) {
+               g_signal_handler_disconnect (sidebar->bookmarks,
+                                            sidebar->bookmarks_changed_id);
+               sidebar->bookmarks_changed_id = 0;
+       }
+
+       g_clear_object (&sidebar->store);
+       g_clear_object (&sidebar->volume_monitor);
+       g_clear_object (&sidebar->bookmarks);
+       g_clear_object (&sidebar->filter_model);
+
+       eel_remove_weak_pointer (&(sidebar->go_to_after_mount_slot));
+
+       g_signal_handlers_disconnect_by_func (nautilus_preferences,
+                                             desktop_setting_changed_callback,
+                                             sidebar);
+
+       g_signal_handlers_disconnect_by_func (nautilus_preferences,
+                                             bookmarks_popup_menu_detach_cb,
+                                             sidebar);
+
+       g_signal_handlers_disconnect_by_func (gnome_background_preferences,
+                                             desktop_setting_changed_callback,
+                                             sidebar);
+
+       G_OBJECT_CLASS (nautilus_places_sidebar_parent_class)->dispose (object);
+}
+
+static void
+nautilus_places_sidebar_class_init (NautilusPlacesSidebarClass *class)
+{
+       G_OBJECT_CLASS (class)->dispose = nautilus_places_sidebar_dispose;
+
+       GTK_WIDGET_CLASS (class)->style_set = nautilus_places_sidebar_style_set;
+}
+
+static void
+nautilus_places_sidebar_set_parent_window (NautilusPlacesSidebar *sidebar,
+                                          NautilusWindow *window)
+{
+       NautilusWindowSlot *slot;
+
+       sidebar->window = window;
+
+       slot = nautilus_window_get_active_slot (window);
+
+       sidebar->bookmarks = nautilus_bookmark_list_new ();
+       sidebar->uri = nautilus_window_slot_get_current_uri (slot);
+
+       sidebar->bookmarks_changed_id =
+               g_signal_connect_swapped (sidebar->bookmarks, "changed",
+                                         G_CALLBACK (update_places),
+                                         sidebar);
+
+       g_signal_connect_object (window, "loading_uri",
+                                G_CALLBACK (loading_uri_callback),
+                                sidebar, 0);
+                        
+       g_signal_connect_object (sidebar->volume_monitor, "volume_added",
+                                G_CALLBACK (volume_added_callback), sidebar, 0);
+       g_signal_connect_object (sidebar->volume_monitor, "volume_removed",
+                                G_CALLBACK (volume_removed_callback), sidebar, 0);
+       g_signal_connect_object (sidebar->volume_monitor, "volume_changed",
+                                G_CALLBACK (volume_changed_callback), sidebar, 0);
+       g_signal_connect_object (sidebar->volume_monitor, "mount_added",
+                                G_CALLBACK (mount_added_callback), sidebar, 0);
+       g_signal_connect_object (sidebar->volume_monitor, "mount_removed",
+                                G_CALLBACK (mount_removed_callback), sidebar, 0);
+       g_signal_connect_object (sidebar->volume_monitor, "mount_changed",
+                                G_CALLBACK (mount_changed_callback), sidebar, 0);
+       g_signal_connect_object (sidebar->volume_monitor, "drive_disconnected",
+                                G_CALLBACK (drive_disconnected_callback), sidebar, 0);
+       g_signal_connect_object (sidebar->volume_monitor, "drive_connected",
+                                G_CALLBACK (drive_connected_callback), sidebar, 0);
+       g_signal_connect_object (sidebar->volume_monitor, "drive_changed",
+                                G_CALLBACK (drive_changed_callback), sidebar, 0);
+
+       g_signal_connect_swapped (nautilus_preferences, "changed::" NAUTILUS_PREFERENCES_ALWAYS_USE_BROWSER,
+                                 G_CALLBACK (bookmarks_popup_menu_detach_cb), sidebar);
+
+       update_places (sidebar);
+}
+
+static void
+nautilus_places_sidebar_style_set (GtkWidget *widget,
+                                  GtkStyle  *previous_style)
+{
+       NautilusPlacesSidebar *sidebar;
+
+       sidebar = NAUTILUS_PLACES_SIDEBAR (widget);
+
+       update_places (sidebar);
+}
+
+GtkWidget *
+nautilus_places_sidebar_new (NautilusWindow *window)
+{
+       NautilusPlacesSidebar *sidebar;
+       
+       sidebar = g_object_new (nautilus_places_sidebar_get_type (), NULL);
+       nautilus_places_sidebar_set_parent_window (sidebar, window);
+
+       return GTK_WIDGET (sidebar);
+}
+
+
+/* Drag and drop interfaces */
+
+static void
+_nautilus_shortcuts_model_filter_class_init (NautilusShortcutsModelFilterClass *class)
+{
+}
+
+static void
+_nautilus_shortcuts_model_filter_init (NautilusShortcutsModelFilter *model)
+{
+       model->sidebar = NULL;
+}
+
+/* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
+static gboolean
+nautilus_shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
+                                              GtkTreePath       *path)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       PlaceType place_type;
+       SectionType section_type;
+
+       model = GTK_TREE_MODEL (drag_source);
+
+       gtk_tree_model_get_iter (model, &iter, path);
+       gtk_tree_model_get (model, &iter,
+                           PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
+                           PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &section_type,
+                           -1);
+
+       if (place_type != PLACES_HEADING && section_type == SECTION_BOOKMARKS)
+               return TRUE;
+
+       return FALSE;
+}
+
+/* Fill the GtkTreeDragSourceIface vtable */
+static void
+nautilus_shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
+{
+       iface->row_draggable = nautilus_shortcuts_model_filter_row_draggable;
+}
+
+static GtkTreeModel *
+nautilus_shortcuts_model_filter_new (NautilusPlacesSidebar *sidebar,
+                                    GtkTreeModel          *child_model,
+                                    GtkTreePath           *root)
+{
+       NautilusShortcutsModelFilter *model;
+
+       model = g_object_new (NAUTILUS_SHORTCUTS_MODEL_FILTER_TYPE,
+                             "child-model", child_model,
+                             "virtual-root", root,
+                             NULL);
+
+       model->sidebar = sidebar;
+
+       return GTK_TREE_MODEL (model);
+}
diff --git a/gtk/gtkplacessidebar.h b/gtk/gtkplacessidebar.h
new file mode 100644 (file)
index 0000000..470b825
--- /dev/null
@@ -0,0 +1,50 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/*
+ *  Nautilus
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Author : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk)
+ *
+ */
+#ifndef _NAUTILUS_PLACES_SIDEBAR_H
+#define _NAUTILUS_PLACES_SIDEBAR_H
+
+#include "nautilus-window.h"
+
+#include <gtk/gtk.h>
+
+#define NAUTILUS_PLACES_SIDEBAR_ID    "places"
+
+#define NAUTILUS_TYPE_PLACES_SIDEBAR nautilus_places_sidebar_get_type()
+#define NAUTILUS_PLACES_SIDEBAR(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_PLACES_SIDEBAR, NautilusPlacesSidebar))
+#define NAUTILUS_PLACES_SIDEBAR_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_PLACES_SIDEBAR, NautilusPlacesSidebarClass))
+#define NAUTILUS_IS_PLACES_SIDEBAR(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_PLACES_SIDEBAR))
+#define NAUTILUS_IS_PLACES_SIDEBAR_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_PLACES_SIDEBAR))
+#define NAUTILUS_PLACES_SIDEBAR_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_PLACES_SIDEBAR, NautilusPlacesSidebarClass))
+
+
+GType nautilus_places_sidebar_get_type (void);
+GtkWidget * nautilus_places_sidebar_new (NautilusWindow *window);
+
+
+#endif